<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.10">
<meta name="author" content="Andreas Falk">
<link rel="icon" type="image/png" href="images/favicon.png">
<title>Reactive Spring Security 5 Hands-On Workshop</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment @import statement to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite::before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
:not(pre)>code.nobreak{word-wrap:normal}
:not(pre)>code.nowrap{white-space:nowrap}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em}
pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em}
pre.pygments .lineno::before{content:"";margin-right:-.125em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0}
table.tableblock{max-width:100%;border-collapse:separate}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
td.tableblock>.content>:last-child.sidebarblock{margin-bottom:0}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}
table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}
table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}
table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px}
table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0}
table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}
table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot,table.frame-ends{border-width:1px 0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body class="book toc2 toc-left">
<div id="header">
<h1>Reactive Spring Security 5 Hands-On Workshop</h1>
<div class="details">
<span id="author" class="author">Andreas Falk</span><br>
<span id="email" class="email"><a href="mailto:andreas.falk@novatec-gmbh.de">andreas.falk@novatec-gmbh.de</a></span><br>
<span id="revnumber">version 1.0,</span>
<span id="revdate">02.09.2020</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_introduction">1. Introduction</a>
<ul class="sectlevel2">
<li><a href="#_requirements_for_this_workshop">1.1. Requirements for this workshop</a></li>
<li><a href="#_general_setup_for_this_workshop">1.2. General Setup for this workshop</a>
<ul class="sectlevel3">
<li><a href="#_intellij">1.2.1. IntelliJ</a></li>
<li><a href="#_eclipse_ide">1.2.2. Eclipse IDE</a></li>
<li><a href="#_visual_studio_code">1.2.3. Visual Studio Code</a></li>
<li><a href="#_get_the_source_code">1.2.4. Get the source code</a></li>
<li><a href="#_run_the_java_applications">1.2.5. Run the java applications</a></li>
</ul>
</li>
<li><a href="#_setup_mongodb_compass">1.3. Setup MongoDB Compass</a></li>
<li><a href="#_setup_keycloak">1.4. Setup Keycloak</a>
<ul class="sectlevel3">
<li><a href="#_using_docker">1.4.1. Using Docker</a></li>
<li><a href="#_local_installation">1.4.2. Local Installation</a></li>
<li><a href="#_startup_keycloak">1.4.3. Startup Keycloak</a></li>
<li><a href="#_remap_default_port_of_keycloak">1.4.4. Remap default port of Keycloak</a></li>
<li><a href="#_open_keycloak_admin_ui">1.4.5. Open Keycloak Admin UI</a></li>
<li><a href="#_further_information">1.4.6. Further Information</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_common_web_security_risks">2. Common Web Security Risks</a></li>
<li><a href="#_reactive_systems_streams">3. Reactive Systems &amp; Streams</a>
<ul class="sectlevel2">
<li><a href="#_project_reactor">3.1. Project Reactor</a></li>
<li><a href="#_spring_webflux">3.2. Spring WebFlux</a></li>
<li><a href="#_intro_lab_reactive_programming">3.3. Intro-Lab: Reactive Programming</a></li>
<li><a href="#_intro_lab_migrate_apps_to_reactive">3.4. Intro-Lab: Migrate Apps To Reactive</a></li>
</ul>
</li>
<li><a href="#_the_workshop_application">4. The workshop application</a></li>
<li><a href="#_basic_security_labs">5. Basic Security Labs</a>
<ul class="sectlevel2">
<li><a href="#_run_the_initial_application">5.1. Run the initial application</a></li>
</ul>
</li>
<li><a href="#_lab_1_auto_configuration">6. Lab 1: Auto Configuration</a>
<ul class="sectlevel2">
<li><a href="#_login">6.1. Login</a></li>
<li><a href="#_common_security_problems">6.2. Common Security Problems</a></li>
<li><a href="#_logout">6.3. Logout</a></li>
</ul>
</li>
<li><a href="#_lab_2_customize_authentication">7. Lab 2: Customize Authentication</a>
<ul class="sectlevel2">
<li><a href="#_webfilter">7.1. WebFilter</a></li>
<li><a href="#_webfilterchainproxy">7.2. WebFilterChainProxy</a></li>
<li><a href="#_step_1_encoding_passwords">7.3. Step 1: Encoding Passwords</a></li>
<li><a href="#_step_2_persistent_user_storage">7.4. Step 2: Persistent User Storage</a>
<ul class="sectlevel3">
<li><a href="#_users_and_roles">7.4.1. Users and roles</a></li>
</ul>
</li>
<li><a href="#_automatic_password_encoding_updates">7.5. Automatic Password Encoding Updates</a></li>
</ul>
</li>
<li><a href="#_lab_3_add_authorization">8. Lab 3: Add Authorization</a></li>
<li><a href="#_lab_4_security_testing">9. Lab 4: Security Testing</a></li>
<li><a href="#_oauth2openid_connect_labs">10. OAuth2/OpenID Connect Labs</a>
<ul class="sectlevel2">
<li><a href="#_introduction_2">10.1. Introduction</a>
<ul class="sectlevel3">
<li><a href="#_oauth_2_0">10.1.1. OAuth 2.0</a></li>
<li><a href="#_openid_connect_1_0_oidc">10.1.2. OpenID Connect 1.0 (OIDC)</a></li>
<li><a href="#_tokens_in_oidc_and_oauth_2_0">10.1.3. Tokens in OIDC and OAuth 2.0</a></li>
<li><a href="#_oauth2oidc_in_spring_security_5">10.1.4. OAuth2/OIDC in Spring Security 5</a></li>
<li><a href="#_what_we_will_build">10.1.5. What we will build</a></li>
</ul>
</li>
<li><a href="#identity-server">10.2. Setup: Keycloak Identity Server</a></li>
<li><a href="#auth-code-demo">10.3. Intro-Lab: Authorization Code Demo Client</a></li>
<li><a href="#resource-server">10.4. Lab 5: OpenID Connect Resource Server</a>
<ul class="sectlevel3">
<li><a href="#resource-server-gradle-dependencies">10.4.1. Gradle dependencies</a></li>
<li><a href="#_implementation_steps">10.4.2. Implementation steps</a></li>
</ul>
</li>
<li><a href="#oauth2-login-client">10.5. Lab 6: OpenID Connect Client</a>
<ul class="sectlevel3">
<li><a href="#client-gradle-dependencies">10.5.1. Gradle dependencies</a></li>
<li><a href="#_implementation_steps_2">10.5.2. Implementation steps</a>
<ul class="sectlevel4">
<li><a href="#_run_all_the_components">Run all the components</a></li>
<li><a href="#_logout_users">Logout Users</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><a href="#_feedback">Feedback</a></li>
<li><a href="#_references">References</a></li>
<li><a href="#_copyright_and_license">Appendix A: Copyright and License</a></li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="_introduction"><a class="anchor" href="#_introduction"></a>1. Introduction</h2>
<div class="sectionbody">
<div class="quoteblock">
<blockquote>
From my experience all software developers are now security engineers whether they know it, admit to it or do it.
Your code is now the security of the org you work for.
</blockquote>
<div class="attribution">
&#8212; Jim Manico
</div>
</div>
<div class="paragraph">
<div class="title">Welcome to the <strong>Reactive Spring Security 5 Hands-On Workshop</strong>.</div>
<p>Target of this workshop is to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Learn <a href="https://www.reactivemanifesto.org">Reactive Systems</a> and reactive programming
using <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux">Spring WebFlux</a>
&amp; <a href="https://projectreactor.io">Reactor</a></p>
</li>
<li>
<p>How to make an initially unsecured reactive spring webflux application more and more secure step-by-step.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You will make your hands dirty in code in the following steps:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Perform practical exercises for reactive streams programming using <a href="https://projectreactor.io">Reactor</a></p>
</li>
<li>
<p>Add spring boot security starter dependency for simple auto configuration of security</p>
</li>
<li>
<p>Customize authentication configuration (provide our own user store + encryption)</p>
</li>
<li>
<p>Add authorization (access controls) to web and method layers</p>
</li>
<li>
<p>Implement automated security integration tests</p>
</li>
<li>
<p>Develop an OAuth2 &amp; OpenID Connect Client and Resource Server</p>
</li>
</ol>
</div>
<div class="sect2">
<h3 id="_requirements_for_this_workshop"><a class="anchor" href="#_requirements_for_this_workshop"></a>1.1. Requirements for this workshop</h3>
<div class="ulist">
<ul>
<li>
<p>Git</p>
</li>
<li>
<p>A Java JDK (Java 8, 11 or 14 are supported and tested)</p>
</li>
<li>
<p>Any Java IDE capable of building with <a href="https://gradle.org/">Gradle</a> (IntelliJ, Eclipse, VS Code, &#8230;&#8203;)</p>
</li>
<li>
<p>Java programming skills, recommended is some knowledge in Spring Boot and the Spring Framework</p>
</li>
<li>
<p><a href="https://www.getpostman.com/downloads">Postman</a>, <a href="https://curl.haxx.se">Curl</a> or <a href="https://httpie.org">Httpie</a> to call the REST API from command line</p>
</li>
<li>
<p><a href="https://www.mongodb.com/try/download/compass">MongoDB Compass</a> or <a href="https://robomongo.org">Robo 3T</a> to look inside the embedded MongoDB instance</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_general_setup_for_this_workshop"><a class="anchor" href="#_general_setup_for_this_workshop"></a>1.2. General Setup for this workshop</h3>
<div class="sect3">
<h4 id="_intellij"><a class="anchor" href="#_intellij"></a>1.2.1. IntelliJ</h4>
<div class="paragraph">
<p>IntelliJ does not require any specific additional plugins or configuration.</p>
</div>
</div>
<div class="sect3">
<h4 id="_eclipse_ide"><a class="anchor" href="#_eclipse_ide"></a>1.2.2. Eclipse IDE</h4>
<div class="paragraph">
<p>If you are an Eclipse user, then the usage of the Eclipse-based <a href="https://spring.io/tools">Spring ToolSuite</a> is strongly recommended.
This eclipse variant already has all the required gradle and spring boot support pre-installed.</p>
</div>
<div class="paragraph">
<p>In case you want to stick to your plain Eclipse installation then you have to add the following features via the
eclipse marketplace:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>BuildShip Gradle Integration (Version 3.x). This might be already pre-installed depending
on your eclipse variant (e.g. Eclipse JavaEE) installed.</p>
</li>
<li>
<p>Spring Tools 4 for Spring Boot (Spring Tool Suite 4).</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="images/eclipse_gradle.png" alt="eclipse_gradle">
</div>
<div class="title">Figure 1. Eclipse Marketplace for Gradle integration</div>
</div>
</div>
<div class="sect3">
<h4 id="_visual_studio_code"><a class="anchor" href="#_visual_studio_code"></a>1.2.3. Visual Studio Code</h4>
<div class="paragraph">
<p>To be able to work properly in Visual Studio Code with this Spring Boot Java Gradle project you need at least these extensions:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Java Extension Pack</p>
</li>
<li>
<p>vscode-gradle-language</p>
</li>
<li>
<p>VS Code Spring Boot Application Development Extension Pack</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_get_the_source_code"><a class="anchor" href="#_get_the_source_code"></a>1.2.4. Get the source code</h4>
<div class="paragraph">
<p>Clone this GitHub repository (<a href="https://github.com/andifalk/reactive-spring-security-5-workshop" class="bare">https://github.com/andifalk/reactive-spring-security-5-workshop</a>):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">git clone https://github.com/andifalk/reactive-spring-security-5-workshop.git reactive_workshop</code></pre>
</div>
</div>
<div class="paragraph">
<p>or simply download it as a <a href="https://github.com/andifalk/reactive-spring-security-5-workshop/archive/master.zip">zip archive</a>.</p>
</div>
<div class="paragraph">
<p>After that you can import the whole workshop project directory into your IDE as a <em>gradle project</em>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.jetbrains.com/idea">IntelliJ</a>: Open menu item "New project from existing sources&#8230;&#8203;" and then select 'Gradle' when prompted</p>
</li>
<li>
<p><a href="https://www.eclipse.org/">Eclipse</a> or <a href="https://spring.io/tools">Spring ToolSuite</a>: Open menu item "Import/Gradle/Existing gradle project"</p>
</li>
<li>
<p><a href="https://code.visualstudio.com/">Visual Studio Code</a>: Just open the root directory in VS Code and wait until VS Code has configured the project</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_run_the_java_applications"><a class="anchor" href="#_run_the_java_applications"></a>1.2.5. Run the java applications</h4>
<div class="paragraph">
<p>All spring boot based java projects can either be run using your Java IDE or using the command line
with changing into the corresponding project directory and issuing a <code>gradlew bootRun</code> command.</p>
</div>
<div class="paragraph">
<p>For other demo applications like the ones for Micronaut or Quarkus please consult written instructions there.</p>
</div>
<div class="paragraph">
<p>In this workshop we will use <a href="https://keycloak.org">Keycloak</a> by JBoss/RedHat as local identity provider.
<a href="https://keycloak.org">Keycloak</a> is <a href="https://openid.net/developers/certified">certified for OpenID Connect 1.0</a> and
implements OAuth 2.0 and OpenID Connect 1.0.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_setup_mongodb_compass"><a class="anchor" href="#_setup_mongodb_compass"></a>1.3. Setup MongoDB Compass</h3>
<div class="paragraph">
<p>To look inside the MongoDB instance you may use <a href="https://www.mongodb.com/try/download/compass">MongoDB Compass</a> or <a href="https://robomongo.org">Robo 3T</a>.</p>
</div>
<div class="paragraph">
<p>Here we will use MongoDB Compass (this is completely free).
To install this just go to <a href="https://www.mongodb.com/try/download/compass">MongoDB Compass</a> and download the tool for your operating system.
Please use the latest stable version (not the <em>readonly</em> or <em>isolated</em> versions).</p>
</div>
<div class="paragraph">
<p>To connect to the embedded MongoDB of our hands-on labs later use this connection url <code>mongodb://localhost:40495</code> (this will only work when you have started the corresponding spring application later.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/mongodb_compass.png" alt="mongodb_compass">
</div>
<div class="title">Figure 2. MongoDB Compass</div>
</div>
</div>
<div class="sect2">
<h3 id="_setup_keycloak"><a class="anchor" href="#_setup_keycloak"></a>1.4. Setup Keycloak</h3>
<div class="paragraph">
<p>You need a compliant OAuth 2.0 / OpenID Connect provider for this workshop.
Here we will use <a href="https://keycloak.org">Keycloak</a> by RedHat/JBoss.</p>
</div>
<div class="paragraph">
<p>To set up Keycloak you have 2 options:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Run Keycloak using Docker (if you have Docker installed)</p>
</li>
<li>
<p>Local Keycloak installation &amp; configuration</p>
</li>
</ol>
</div>
<div class="sect3">
<h4 id="_using_docker"><a class="anchor" href="#_using_docker"></a>1.4.1. Using Docker</h4>
<div class="paragraph">
<p>If you have Docker installed then setting up Keycloak is quite easy.</p>
</div>
<div class="paragraph">
<p>To configure and run Keycloak using docker:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Open a new command line terminal window</p>
</li>
<li>
<p>Change directory to subdirectory <em>setup_keycloak</em> of the workshop repository</p>
</li>
<li>
<p>Open and edit the script <em>run_keycloak_docker.sh</em> or <em>run_keycloak_docker.bat</em> (depending on your OS) and adapt the value for the <em>WORKSHOP_HOME</em> env variable to your local workshop repository directory</p>
</li>
<li>
<p>Save and execute the script <em>run_keycloak_docker.sh</em> or <em>run_keycloak_docker.bat</em> (depending on your OS)</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Wait until the docker container has been started completely. When you see the line <em>Started 590 of 885 services</em>,
then Keycloak is configured and running.
Now open your web browser and navigate to <a href="http://localhost:8080/auth/admin">localhost:8080/auth/admin</a> and login
using the user credentials <em>admin</em>/<em>admin</em>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_local_installation"><a class="anchor" href="#_local_installation"></a>1.4.2. Local Installation</h4>
<div class="paragraph">
<p>To set up <a href="https://keycloak.org">Keycloak</a>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Download the <a href="https://www.keycloak.org/downloads-archive.html">Standard Server Distribution of Keycloak (Version 10.0.x)</a>.</p>
</li>
<li>
<p>Extract the downloaded zip/tar file <em>keycloak-x.x.x.zip</em>/<em>keycloak-x.x.x.tar-gz</em> into a new local directory of your choice
(this directory will be referenced as <em>&lt;KEYCLOAK_INSTALL_DIR&gt;</em> in next steps)</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>This workshop requires a pre-defined configuration for Keycloak (i.e. some OAuth2/OpenID Connect clients, and user accounts).</p>
</div>
<div class="paragraph">
<p>To configure Keycloak you need to have checked out the GIT repository for this workshop.
All you need to configure Keycloak is located in the subdirectory <em>setup_keycloak</em> of the repository.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Change into the subdirectory <em>setup_keycloak</em> of the workshop git repository</p>
</li>
<li>
<p>Open the file <em>import_keycloak_realm.sh</em> or <em>import_keycloak_realm.bat</em> (depending on your OS) in the <em>setup_keycloak</em> subdirectory
and change the value of the environment variable <em>KEYCLOAK_HOME</em> to your <em>&lt;KEYCLOAK_INSTALL_DIR&gt;</em> of step 2 and save the file</p>
</li>
<li>
<p>Now open a new command-line terminal window, change into the subdirectory <em>setup_keycloak</em> again and execute the provided script
<em>import_keycloak_realm.sh</em> or <em>import_keycloak_realm.bat</em> (depending on your OS).
This starts a standalone Keycloak instance and automatically imports the required configuration.</p>
</li>
<li>
<p>Wait until the import has finished (look for a line like <em>Started 590 of 885 services</em>) then
direct your web browser to [localhost:8080/auth](<a href="http://localhost:8080/auth/" class="bare">http://localhost:8080/auth/</a>)</p>
</li>
<li>
<p>Here you have to create the initial admin user to get started. Please use the value <em>admin</em> both as username and as password,
then click the button <em>Create</em>. Please note: In production you must use a much more secure password for the admin user!</p>
</li>
<li>
<p>Now you can continue to the <em>Administration Console</em> by clicking on the corresponding link displayed and login using the new user credentials.</p>
</li>
</ol>
</div>
<div class="imageblock">
<div class="content">
<img src="images/keycloak_initial_admin.png" alt="eclipse_gradle">
</div>
<div class="title">Figure 3. Keycloak Administrator Initialization</div>
</div>
<div class="paragraph">
<p>If all worked successfully you should see the settings page of the <em>Workshop</em> realm and Keycloak is ready for this Workshop !</p>
</div>
</div>
<div class="sect3">
<h4 id="_startup_keycloak"><a class="anchor" href="#_startup_keycloak"></a>1.4.3. Startup Keycloak</h4>
<div class="paragraph">
<p>You only have to do the initial setup section for local install once.
If you have stopped Keycloak and want to start it again then follow the next lines in this section.</p>
</div>
<div class="paragraph">
<p>To startup <a href="https://keycloak.org">Keycloak</a>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Open a terminal and change directory to sub directory <em>&lt;KEYCLOAK_INSTALL_DIR&gt;/bin</em> and start Keycloak using
the <em>standalone.sh</em>(Linux or Mac OS) or <em>standalone.bat</em> (Windows) scripts</p>
</li>
<li>
<p>Wait until keycloak has been started completely - you should see something like this <code>&#8230;&#8203;(WildFly Core &#8230;&#8203;) started in 6902ms - Started 580 of 842 services</code></p>
</li>
</ol>
</div>
</div>
<div class="sect3">
<h4 id="_remap_default_port_of_keycloak"><a class="anchor" href="#_remap_default_port_of_keycloak"></a>1.4.4. Remap default port of Keycloak</h4>
<div class="paragraph">
<p>In case port <em>8080</em> does not work on your local machine (i.e. is used by another process) then you may have to change Keycloak to use another port.
This can be done like this (e.g. for remapping port to 8090 instead of 8080):</p>
</div>
<div class="paragraph">
<p>On Linux/MAC:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">./standalone.sh -Djboss.socket.binding.port-offset=10</code></pre>
</div>
</div>
<div class="paragraph">
<p>On Windows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">./standalone.bat -Djboss.socket.binding.port-offset=10</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note: Take into account that for all URL&#8217;s pointing to Keycloak in the hands-on steps you always have to use the remapped port
instead of default one (8080) as well.</p>
</div>
</div>
<div class="sect3">
<h4 id="_open_keycloak_admin_ui"><a class="anchor" href="#_open_keycloak_admin_ui"></a>1.4.5. Open Keycloak Admin UI</h4>
<div class="paragraph">
<p>Independent of the setup type (docker or local install), to access the web admin UI of Keycloak
you need to perform these steps:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Now direct your browser to [localhost:8080/auth/admin](<a href="http://localhost:8080/auth/admin/" class="bare">http://localhost:8080/auth/admin/</a>)</p>
</li>
<li>
<p>Login into the admin console using <em>admin/admin</em> as credentials</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Now, if you see the realm <em>workshop</em> on the left then Keycloak is ready to use it for this workshop.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/keycloak_workshop.png" alt="eclipse_gradle">
</div>
<div class="title">Figure 4. Keycloak Workshop Realm</div>
</div>
</div>
<div class="sect3">
<h4 id="_further_information"><a class="anchor" href="#_further_information"></a>1.4.6. Further Information</h4>
<div class="paragraph">
<p>If you want to know more about setting up a Keycloak server for your own projects
then please consult the <a href="https://www.keycloak.org/docs/latest/server_admin/index.html">keycloak administration docs</a>.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_common_web_security_risks"><a class="anchor" href="#_common_web_security_risks"></a>2. Common Web Security Risks</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In this workshop you will strive various parts of securing a web application that
fit into the <a href="https://www.owasp.org/index.php/Top_10-2017_Top_10">OWASP Top 10 2017 list</a>.</p>
</div>
<div class="paragraph">
<p>We will look at:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A2-Broken_Authentication">A2: Broken Authentication</a></p>
</li>
<li>
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A3-Sensitive_Data_Exposure">A3: Sensitive Data Exposure</a></p>
</li>
<li>
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A5-Broken_Access_Control">A5: Broken Access Control</a></p>
</li>
<li>
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A6-Security_Misconfiguration">A6: Security Misconfiguration</a></p>
</li>
<li>
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A9-Using_Components_with_Known_Vulnerabilities">A9: Using components with known vulnerabilities</a></p>
</li>
<li>
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A10-Insufficient_Logging%26Monitoring">A10: Insufficient Logging &amp; Monitoring</a></p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="images/owasp_top_10_2017.png" alt="owasp_top_10">
</div>
<div class="title">Figure 5. OWASP Top 10 2017</div>
</div>
<div class="paragraph">
<p>The <a href="https://owasp.org">Open Web Application Security Project</a> has plenty of further free resources available.</p>
</div>
<div class="paragraph">
<p>As a developer you may also have a look into the <a href="https://www.owasp.org/index.php/OWASP_Proactive_Controls">OWASP ProActive Controls</a> document which describes how to develop
your applications using good security patterns.
To help getting all security requirements right for your project and how to test these the <a href="https://github.com/OWASP/ASVS">Application Security Verification Standard</a> can help you here.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You will find more sources of information about security referenced in the <a href="#_references">References</a> section.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_reactive_systems_streams"><a class="anchor" href="#_reactive_systems_streams"></a>3. Reactive Systems &amp; Streams</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The following subsections give a very condensed introduction to the basics of Reactive Systems,
the Project Reactor and Spring WebFlux.<br>
This might help to better understand the sample application for beginners of Reactive.</p>
</div>
<div class="quoteblock">
<blockquote>
Reactive Systems are Responsive, Resilient, Elastic and Message Driven (Asynchronous).
</blockquote>
<div class="attribution">
&#8212; https://www.reactivemanifesto.org
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Responsiveness</strong> means the system responds in a timely manner and is the cornerstone of usability</p>
</li>
<li>
<p><strong>Resilience</strong> means the system stays responsive in the face of failure</p>
</li>
<li>
<p><strong>Elasticity</strong> means that the throughput of a system scales up or down automatically to
meet varying demand as resource is proportionally added or removed</p>
</li>
<li>
<p><strong>Message Driven</strong> systems rely on asynchronous message-passing to establish a boundary between components
that ensures loose coupling</p>
</li>
</ul>
</div>
<div class="quoteblock">
<blockquote>
Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure..
</blockquote>
<div class="attribution">
&#8212; http://www.reactive-streams.org/
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Back-Pressure</strong>: When one component is struggling to keep-up, the system as a whole needs to respond in a sensible way.
Back-pressure is an important feedback mechanism that allows systems to gracefully respond to load rather than
collapse under it.</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_project_reactor"><a class="anchor" href="#_project_reactor"></a>3.1. Project Reactor</h3>
<div class="paragraph">
<p>The project <a href="https://projectreactor.io">Reactor</a> is a Reactive library for building non-blocking applications on
the JVM based on the <a href="http://www.reactive-streams.org">Reactive Streams Specification</a> and can help to build
Reactive Systems.</p>
</div>
<div class="paragraph">
<p>Reactor is a fully non-blocking foundation and offers backpressure-ready
network engines for HTTP (including Websockets), TCP and UDP.</p>
</div>
<div class="paragraph">
<p>Reactor introduces composable reactive types that implement Publisher but also provide a rich vocabulary of operators,
most notably <em>Flux</em> and <em>Mono</em>.</p>
</div>
<div class="paragraph">
<p>A <em>Mono&lt;T&gt;</em> is a specialized <em>Publisher&lt;T&gt;</em> that emits at most one item and then optionally terminates with
an onComplete signal or an onError signal.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/mono.png" alt="reactor_mono">
</div>
<div class="title">Figure 6. Mono, an Asynchronous 0-1 Result (source: projectreactor.io)</div>
</div>
<div class="paragraph">
<p>A <em>Flux&lt;T&gt;</em> is a standard <em>Publisher&lt;T&gt;</em> representing an asynchronous sequence of 0 to N emitted items,
optionally terminated by either a completion signal or an error.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/flux.png" alt="reactor_flux">
</div>
<div class="title">Figure 7. Flux, an Asynchronous Sequence of 0-N Items (source: projectreactor.io)</div>
</div>
</div>
<div class="sect2">
<h3 id="_spring_webflux"><a class="anchor" href="#_spring_webflux"></a>3.2. Spring WebFlux</h3>
<div class="paragraph">
<p><a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux">Spring WebFlux</a>
was added in Spring Framework 5.0. It is fully non-blocking, supports Reactive Streams back pressure, and runs
on servers such as Netty, Undertow, and Servlet 3.1+ containers.</p>
</div>
<div class="paragraph">
<p>Spring Webflux depends on Reactor and uses it internally to compose asynchronous logic
and to provide Reactive Streams support.
It provides two programming models:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Annotated Controllers: This is uses the same annotations as in the Spring MVC part</p>
</li>
<li>
<p>Functional Endpoints:  This provides a lambda-based, lightweight, functional programming model</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_intro_lab_reactive_programming"><a class="anchor" href="#_intro_lab_reactive_programming"></a>3.3. Intro-Lab: Reactive Programming</h3>
<div class="paragraph">
<p>Before we dive into the world of security, you have the chance to get a first glimpse on the difference between
imperative and reactive programming style.</p>
</div>
<div class="paragraph">
<p>To do this we will look into the project <strong>intro-labs/reactive-playground</strong>.</p>
</div>
<div class="paragraph">
<p>The following resources might be helpful for first steps into the reactive world:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://projectreactor.io/docs/core/release/reference/">Project Reactor Reference Documentation</a></p>
</li>
<li>
<p><a href="https://projectreactor.io/docs/core/release/reference/#which-operator">Which operator do I need?</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_intro_lab_migrate_apps_to_reactive"><a class="anchor" href="#_intro_lab_migrate_apps_to_reactive"></a>3.4. Intro-Lab: Migrate Apps To Reactive</h3>
<div class="paragraph">
<p>When moving in the direction of the reactive world, you usually have existing applications still running on blocking servlet stack.</p>
</div>
<div class="paragraph">
<p>Important to mention is also the fact that you do <strong>NOT</strong> have to migrate all you applications to reactive.
The servlet based stack has proven to work for most applications so this won&#8217;t go away. If you do not have a present problem with the load on your server application with
threads being exhausted then there is no need to act.</p>
</div>
<div class="paragraph">
<p>To show a sample migration scenario, we will migrate the following applications to their reactive counterparts:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A server web application using Spring MVC with the blocking servlet stack on Tomcat</p>
</li>
<li>
<p>A client web application using a blocking <code>RestTemplate</code> to call the server application</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>We will now migrate these to Spring WebFlux (the server application) and to using the reactive <code>WebClient</code> (the client application).</p>
</div>
<div class="paragraph">
<p>Let&#8217;s start by looking into the folder <strong>intro-labs/migrate-to-reactive</strong>.</p>
</div>
<div class="paragraph">
<p>See the spring reference docs for full coverage of <em>Spring WebFlux</em> and the <code>WebClient</code>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux">Spring Reference Docs</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_workshop_application"><a class="anchor" href="#_the_workshop_application"></a>4. The workshop application</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In this workshop you will be provided a finished but completely unsecured reactive
web application.
This library server application provides a <a href="https://martinfowler.com/articles/richardsonMaturityModel.html">RESTful service</a>
for administering books and users.</p>
</div>
<div class="paragraph">
<p>You can find this provided workshop application in sub project <strong>lab-1/initial-library-server</strong>.</p>
</div>
<div class="paragraph">
<p>This will also be your <strong>starting point</strong> into the hands-on part that we will dive into shortly.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/workshop_lab_1.png" alt="Library service">
</div>
<div class="title">Figure 8. The workshop application</div>
</div>
<div class="paragraph">
<p>The RESTful service for books is build using the Spring WebFlux annotation model and the RESTful service for
users is build using the Spring WebFlux router model.</p>
</div>
<div class="paragraph">
<p>The application contains a complete documentation for the RESTful API build with spring rest docs
which you can find in the directory <em>build/asciidoc/html5</em>
after performing a full gradle build.</p>
</div>
<div class="paragraph">
<p>The domain model of this application is quite simple and just consists of <em>Book</em> and <em>User</em>.
The packages of the application are organized as follows:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>api</strong>: Contains the complete RESTful service</p>
</li>
<li>
<p><strong>business</strong>: All the service classes (quite simple for workshop, usually containing business logic)</p>
</li>
<li>
<p><strong>dataaccess</strong>: All domain models and repositories</p>
</li>
<li>
<p><strong>config</strong>: All spring configuration classes</p>
</li>
<li>
<p><strong>common</strong>: Common classes used by several other packages</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="images/workshop_lab_2.png" alt="Library service stack">
</div>
<div class="title">Figure 9. Library service stack</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>To call the provided REST API you can use <a href="https://curl.haxx.se">curl</a> or <a href="https://httpie.org">httpie</a>.
For details on how to call the REST API please consult
the <a href="https://andifalk.github.io/reactive-spring-security-5-workshop/api-doc.html">REST API documentation</a>
which also provides sample requests for <em>curl</em> and <em>httpie</em>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>There are three target user roles for this application:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Standard users</strong>: A standard user can borrow and return his currently borrowed books</p>
</li>
<li>
<p><strong>Curators</strong>: A curator user can add or delete books</p>
</li>
<li>
<p><strong>Administrators</strong>: An administrator user can add or remove users</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If you are going into reactive systems this works best if all layers work in non-blocking reactive style.
Therefore, the application is build using:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Spring 5 WebFlux on Netty</p>
</li>
<li>
<p>Spring Data MongoDB with reactive driver</p>
</li>
<li>
<p>In-memory Mongodb to have an easier setup for the workshop</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_basic_security_labs"><a class="anchor" href="#_basic_security_labs"></a>5. Basic Security Labs</h2>
<div class="sectionbody">
<div class="paragraph">
<p>To start the workshop please begin by adapting the <strong>lab-1/initial-library-server</strong></p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you are not able to keep up with completing a particular step you
always can just start over with the existing application of next step.</p>
</div>
<div class="paragraph">
<p>For example if you could not complete the lab 1 in time
just continue with lab 2 using <strong>lab-1/complete-library-server</strong> as new starting point.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_run_the_initial_application"><a class="anchor" href="#_run_the_initial_application"></a>5.1. Run the initial application</h3>
<div class="paragraph">
<p>To ensure your java environment is configured correctly please run the initial
workshop application.</p>
</div>
<div class="paragraph">
<p>To achieve this run the class <em>com.example.library.server.InitialLibraryServerApplication</em> in project <strong>lab-1/initial-library-server</strong>.</p>
</div>
<div class="paragraph">
<p>This should also start the embedded MongoDB instance. In case you get an error here telling that the corresponding port <em>40495</em>
is already bound to another service then please change the configuration to a different port in file <em>application.yml</em>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">spring</span>:
  <span class="key">data</span>:
    <span class="key">mongodb</span>:
      <span class="key">port</span>: <span class="string"><span class="content">40495</span></span></code></pre>
</div>
</div>
<div class="paragraph">
<p>To look into the embedded MongoDB instance the UI tool <a href="https://robomongo.org/">Robo 3T</a> is
very helpful and easy to use. In case you did not yet install this tool just go to
<a href="https://robomongo.org/download" class="bare">https://robomongo.org/download</a> and download the corresponding file for your operating system
(please do NOT download the commercial variant named <em>Studio 3T</em>).</p>
</div>
<div class="paragraph">
<p>After you downloaded the install file and extracted/installed the tool you have to configure
the connection to the embedded MongoDB like in the following figure.
If you have configured your embedded MongoDB instance to a different port then use that one
instead of <em>40495</em>.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/robo3t.png" alt="Library service stack">
</div>
<div class="title">Figure 10. Robo 3T Connection configuration</div>
</div>
<div class="paragraph">
<p>Finally, you can navigate your web browser to <a href="http://localhost:9091/books" class="bare">http://localhost:9091/books</a> then you
should see a list of books.</p>
</div>
<div class="paragraph">
<p>You can achieve the same on the command line using <a href="https://httpie.org/">httpie</a>
or <a href="https://curl.haxx.se/download.html">curl</a>.</p>
</div>
<div class="listingblock">
<div class="title">Curl</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">curl 'http://localhost:9091/books' | jq</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">Httpie</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">http localhost:9091/books</code></pre>
</div>
</div>
<div class="paragraph">
<p>If both the workshop application and the Robo 3T tool run fine and you could see
the list of books then you are setup and ready to start the first lab.</p>
</div>
<div class="paragraph">
<p>So head over to the next section and start with Lab 1.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_lab_1_auto_configuration"><a class="anchor" href="#_lab_1_auto_configuration"></a>6. Lab 1: Auto Configuration</h2>
<div class="sectionbody">
<div class="paragraph">
<div class="title">In the first step we start quite easy by just adding the spring boot starter dependency for spring security.</div>
<p>We just need to add the following two dependencies to the <em>build.gradle</em> file of the initial application (lab-1/initial-library-server_).</p>
</div>
<div class="listingblock">
<div class="title">build.gradle</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code data-lang="groovy">dependencies {
    ...
    implementation(<span class="string"><span class="delimiter">'</span><span class="content">org.springframework.boot:spring-boot-starter-security</span><span class="delimiter">'</span></span>) <i class="conum" data-value="1"></i><b>(1)</b>
    ...
    testImplementation(<span class="string"><span class="delimiter">'</span><span class="content">org.springframework.security:spring-security-test</span><span class="delimiter">'</span></span>) <i class="conum" data-value="2"></i><b>(2)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The spring boot starter for spring security</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Adds testing support for spring security</td>
</tr>
</table>
</div>
<div class="imageblock">
<div class="content">
<img src="images/workshop_lab_3.png" alt="Library service authentication">
</div>
<div class="title">Figure 11. Authentication using in-memory user</div>
</div>
<div class="paragraph">
<p>Please start the application by running the class <em>InitialLibraryServerApplication</em>.</p>
</div>
<div class="sect2">
<h3 id="_login"><a class="anchor" href="#_login"></a>6.1. Login</h3>
<div class="paragraph">
<p>Spring Security 5 added a nicer auto-generated login form (build with bootstrap library).</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/loginform.png" alt="owasp_top_10">
</div>
<div class="title">Figure 12. Autogenerated login formular</div>
</div>
<div class="paragraph">
<p>If you browse to <a href="http://localhost:8080/books">localhost:8080/books</a> then you will notice
that a login form appears in the browser window.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>But wait - what are the credentials for a user to log in?</p>
</div>
<div class="paragraph">
<p>With spring security autoconfigured by spring boot the credentials are as follows:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Username=user</p>
</li>
<li>
<p>Password=&lt;Look into the console log!&gt;</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">console log</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>INFO 18465 --- [  restartedMain] ctiveUserDetailsServiceAutoConfiguration :

Using default security password: ded10c78-0b2f-4ae8-89fe-c267f9a29e1d</code></pre>
</div>
</div>
<div class="paragraph">
<p>Of course you won&#8217;t use the generated password for any serious application as this will change with each restart of the
application.</p>
</div>
<div class="paragraph">
<p>Instead you can easily just change the password to a static value by changing the <em>application.yaml</em> file.</p>
</div>
<div class="listingblock">
<div class="title">application.yml</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="yaml"><span class="key">spring</span>:
  <span class="error">...</span>
  <span class="key">security</span>:
    <span class="key">user</span>:
      <span class="key">password</span>: <span class="string"><span class="content">secret</span></span></code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Please make sure the indents of the content is correct in yaml formatted files. If this is not the case you can get
really strange errors sometimes.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>As you can see, if Spring Security is on the classpath,
then the web application is secured by default.
<a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-security">Spring boot</a> auto-configured
basic authentication and form based authentication for all web endpoints.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A5-Broken_Access_Control">OWASP Top 10-2017 A5-Broken Access Control</a></p>
</div>
<div class="ulist">
<ul>
<li>
<p>With the exception of public resources, deny by default</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="imageblock">
<div class="content">
<img src="images/session_auth.png" alt="form_login">
</div>
<div class="title">Figure 13. Form-based authentication with session cookies</div>
</div>
<div class="paragraph">
<p>This also applies to all actuator endpoints like <a href="http://localhost:8080/actuator/health">/actuator/health</a>.
All monitoring web endpoints can now only be accessed with an authenticated user.
See <a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-security-actuator">Actuator Security</a>
for details.</p>
</div>
</div>
<div class="sect2">
<h3 id="_common_security_problems"><a class="anchor" href="#_common_security_problems"></a>6.2. Common Security Problems</h3>
<div class="paragraph">
<p>Additionally spring security improved the security of the web application automatically for:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#ns-session-fixation">Session Fixation</a>:
Session Fixation is an attack that permits an attacker to hijack a valid user session.<br>
If you want to learn more about this please read the <a href="https://www.owasp.org/index.php/Session_fixation">Session Fixation page at OWASP</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf">Cross Site Request Forgery (CSRF)</a>:
Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they&#8217;re currently authenticated.<br>
If you want to know what CSRF really is and how to mitigate this attack please consult <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)">CSRF attack description at OWASP</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#default-security-headers">Default Security Headers</a>:
This automatically adds all recommended security response headers to all http responses. You can find more information about this topic
in the <a href="https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Main">OWASP Secure Headers Project</a></p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="title">default security response headers</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: 0
Pragma: no-cache
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_logout"><a class="anchor" href="#_logout"></a>6.3. Logout</h3>
<div class="paragraph">
<p>Spring security 5 also added a bit more user friendly logout functionality out of the box.
If you direct your browser to <a href="http://localhost:8080/logout">localhost:8080/logout</a> you will see the following
dialog on the screen.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/logoutform.png" alt="owasp_top_10">
</div>
<div class="title">Figure 14. Autogenerated logout formular</div>
</div>
<div class="paragraph">
<p>The application already contains a central error handler for the whole web application using <em>@RestControllerAdvice</em>.
You can find this in class <em>com.example.library.server.api.ErrorHandler</em>.</p>
</div>
<div class="paragraph">
<p>To handle potential future access denied error we add the following new block to this class:</p>
</div>
<div class="listingblock">
<div class="title">com.example.library.server.api.ErrorHandler</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">org.springframework.security.access.AccessDeniedException</span>;

<span class="annotation">@RestControllerAdvice</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">ErrorHandler</span> {

  <span class="annotation">@ExceptionHandler</span>(AccessDeniedException.class)
  <span class="directive">public</span> Mono&lt;ResponseEntity&lt;<span class="predefined-type">String</span>&gt;&gt; handle(AccessDeniedException ex) {
    <span class="predefined-type">Logger</span> logger = LoggerFactory.getLogger(<span class="local-variable">this</span>.getClass());
    logger.error(ex.getMessage());
    <span class="keyword">return</span> Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build());
  }
  <span class="comment">// ...</span>
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you try to run the existing tests in package <em>com.example.library.server.api</em> you will
notice that these are not <em>green</em> any more. This is due to the security enforcements
by Spring Security.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now all requests in the tests require an authenticated user and fail with http status 401 (Unauhorized).</p>
</div>
<div class="paragraph">
<p>All tests using requests with <em>POST</em>, <em>PUT</em> or <em>DELETE</em> methods are failing with http status 403 (Forbidden).
These requests are now protected against <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)">CSRF</a> attacks
and require CSRF tokens.</p>
</div>
<div class="paragraph">
<p>To achieve authentication in the tests you have to add the annotation <em>@WithMockUser</em> on class level.
CSRF is handled by mutating the requests inside the tests by adding this snippet to the failing tests:</p>
</div>
<div class="listingblock">
<div class="title">com.example.library.server.api.BookApiDocumentationTest</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//...</span>
<span class="keyword">import</span> <span class="include">static</span> <span class="include">org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf</span>;
<span class="comment">//...</span>
webTestClient.mutateWith(csrf())
        <span class="comment">//...</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This concludes the first step.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You find the completed code in project <strong>lab-1/complete-library-server</strong>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now let&#8217;s proceed to next step and start with customizing the authentication part.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_lab_2_customize_authentication"><a class="anchor" href="#_lab_2_customize_authentication"></a>7. Lab 2: Customize Authentication</h2>
<div class="sectionbody">
<div class="paragraph">
<div class="title">Now it is time to start customizing the auto-configuration.</div>
<p>The spring boot auto-configuration will back-off a bit in this lab and will back-off completely in next step.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/workshop_lab_4.png" alt="Library service custom authentication">
</div>
<div class="title">Figure 15. Custom authentication with persistent users</div>
</div>
<div class="paragraph">
<p>Before we start let&#8217;s look into some internal details how spring security works
for the reactive web stack.</p>
</div>
<div class="sect2">
<h3 id="_webfilter"><a class="anchor" href="#_webfilter"></a>7.1. WebFilter</h3>
<div class="paragraph">
<p>Like the <em>javax.servlet.Filter</em> in the blocking servlet-based web stack there is a comparable functionality
in the reactive world: The <em>WebFilter</em>.</p>
</div>
<div class="listingblock">
<div class="title">WebFilter</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>public interface WebFilter {

        /**
         * Process the Web request and (optionally) delegate to the next
         * {@code WebFilter} through the given {@link WebFilterChain}.
         * @param exchange the current server exchange
         * @param chain provides a way to delegate to the next filter
         * @return {@code Mono&lt;Void&gt;} to indicate when request processing is complete
         */
        Mono&lt;Void&gt; filter(ServerWebExchange exchange, WebFilterChain chain);

}</code></pre>
</div>
</div>
<div class="paragraph">
<p>By using the <em>WebFilter</em> you can add functionality that called around each request and response.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 1. Spring Security WebFilter</caption>
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Filter</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">AuthenticationWebFilter</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Performs authentication of a particular request</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">AuthorizationWebFilter</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Determines if an authenticated user has access to a specific object</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CorsWebFilter</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Handles CORS preflight requests and intercepts</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CsrfWebFilter</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Applies <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)">CSRF</a> protection using a synchronizer token pattern.</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>To see how such a <em>WebFilter</em> works we will implement a simple <em>LoggingWebFilter</em>:</p>
</div>
<div class="listingblock">
<div class="title">LoggingWebFilter</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

@Component
public class LoggingWebFilter implements WebFilter {

    private static Logger LOGGER = LoggerFactory.getLogger(LoggingWebFilter.class);

    @Override
    public Mono&lt;Void&gt; filter(ServerWebExchange exchange, WebFilterChain chain) {
        LOGGER.info(&quot;Request {} called&quot;, exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_webfilterchainproxy"><a class="anchor" href="#_webfilterchainproxy"></a>7.2. WebFilterChainProxy</h3>
<div class="paragraph">
<p>In lab 1 we just used the auto configuration of Spring Boot.
This configured the default security settings as follows:</p>
</div>
<div class="listingblock">
<div class="title">Default security configuration (with Spring Boot)</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>@Configuration
class WebFluxSecurityConfiguration {
   ...

   /**
         * The default {@link ServerHttpSecurity} configuration.
         * @param http
         * @return
         */
        private SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
                http
                        .authorizeExchange()
                                .anyExchange().authenticated();

                if (isOAuth2Present &amp;&amp; OAuth2ClasspathGuard.shouldConfigure(this.context)) {
                        OAuth2ClasspathGuard.configure(this.context, http);
                } else {
                        http
                                .httpBasic().and()
                                .formLogin();
                }

                SecurityWebFilterChain result = http.build();
                return result;
        }

        ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>As you can see this uses a <em>SecurityWebFilterChain</em> as central component.</p>
</div>
<div class="listingblock">
<div class="title">SecurityWebFilterChain</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>/**
 * Defines a filter chain which is capable of being matched against a {@link ServerWebExchange} in order to decide
 * whether it applies to that request.
 *
 * @author Rob Winch
 * @since 5.0
 */
public interface SecurityWebFilterChain {

        /**
         * Determines if this {@link SecurityWebFilterChain} matches the provided {@link ServerWebExchange}
         * @param exchange the {@link ServerWebExchange}
         * @return true if it matches, else false
         */
        Mono&lt;Boolean&gt; matches(ServerWebExchange exchange);

        /**
         * The {@link WebFilter} to use
         * @return
         */
        Flux&lt;WebFilter&gt; getWebFilters();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>To customize the spring security configuration you have to implement one or more
of <em>SecurityWebFilterChain</em> configuration methods.</p>
</div>
<div class="paragraph">
<p>These are handled centrally by the <em>WebFilterChainProxy</em> class.</p>
</div>
<div class="listingblock">
<div class="title">WebFilterChainProxy</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>public class WebFilterChainProxy implements WebFilter {
        private final List&lt;SecurityWebFilterChain&gt; filters;

        public WebFilterChainProxy(List&lt;SecurityWebFilterChain&gt; filters) {
                this.filters = filters;
        }

        public WebFilterChainProxy(SecurityWebFilterChain... filters) {
                this.filters = Arrays.asList(filters);
        }

        @Override
        public Mono&lt;Void&gt; filter(ServerWebExchange exchange, WebFilterChain chain) { <i class="conum" data-value="1"></i><b>(1)</b>
                return Flux.fromIterable(this.filters)
                                .filterWhen( securityWebFilterChain -&gt; securityWebFilterChain.matches(exchange))
                                .next()
                                .switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
                                .flatMap( securityWebFilterChain -&gt; securityWebFilterChain.getWebFilters()
                                        .collectList()
                                )
                                .map( filters -&gt; new FilteringWebHandler(webHandler -&gt; chain.filter(webHandler), filters))
                                .map( handler -&gt; new DefaultWebFilterChain(handler) )
                                .flatMap( securedChain -&gt; securedChain.filter(exchange));
        }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Central point for spring security to step into reactive web requests</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_step_1_encoding_passwords"><a class="anchor" href="#_step_1_encoding_passwords"></a>7.3. Step 1: Encoding Passwords</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A3-Sensitive_Data_Exposure">OWASP Top 10-2017 A3-Sensitive Data Exposure</a></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Make sure to encrypt all sensitive data at rest</p>
</li>
<li>
<p>Store passwords using strong adaptive and salted hashing functions with a work factor (delay factor),
such as Argon2, scrypt, bcrypt or PBKDF2</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We start by replacing the default user/password with our own persistent user storage (already present in MongoDB).
To do this we add a new class <em>WebSecurityConfiguration</em> to package <em>com.example.library.server.config</em> having the following
contents.</p>
</div>
<div class="listingblock">
<div class="title">WebSecurityConfiguration class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebFluxSecurity <i class="conum" data-value="1"></i><b>(1)</b>
public class WebSecurityConfiguration {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder(); <i class="conum" data-value="2"></i><b>(2)</b>
    }

}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This auto-configures the SecurityWebFilterChain</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This adds the new delegating password encoder introduced in spring security 5</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The <em>WebSecurityConfiguration</em> implementation does two important things:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>This adds the <em>SecurityWebFilterChain</em>. If you already have secured servlet based spring mvc web applications
then you might know what&#8217;s called the
<a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#security-filter-chain"><em>spring security filter chain</em></a>.
In spring webflux the <em>SecurityWebFilterChain</em> is the similar approach
based on matching a request with one or more WebFilter.</p>
</li>
<li>
<p>Configures a <em>PasswordEncoder</em>. A password encoder is used by spring security to encode (hash) passwords and to check
if a given password matches the encrypted one.</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="title">PasswordEncoder interface</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package org.springframework.security.crypto.password;

public interface PasswordEncoder {

        String encode(CharSequence rawPassword); <i class="conum" data-value="1"></i><b>(1)</b>

        boolean matches(CharSequence rawPassword, String encodedPassword); <i class="conum" data-value="2"></i><b>(2)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Encrypts the given cleartext password</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Validates the given cleartext password with the encrypted one (without revealing the unencrypted one)</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In spring security 5 creating an instance of the <em>DelegatingPasswordEncoder</em> is much easier
by using the class <em>PasswordEncoderFactories</em>. In past years several previously used password encryption algorithms
have been broken (like <em>MD4</em> or <em>MD5</em>). By using <em>PasswordEncoderFactories</em> you always get a configured
<em>DelegatingPasswordEncoder</em> instance that configures a map of <em>PasswordEncoder</em> instances for the recommended password hashing algorithms like</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://en.wikipedia.org/wiki/Bcrypt">Bcrypt</a></p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Scrypt">Scrypt</a></p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/PBKDF2">PBKDF2</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>At the time of creating this workshop the <em>DelegatingPasswordEncoder</em> instance configures the <em>Bcrypt</em> algorithm
as the default to be used for encoding new passwords.</p>
</div>
<div class="paragraph">
<p>If you want to know more about why to use hashing algorithms like Bcrypt, Scrypt or PBKDF2 instead of other ones like SHA-2 then
read the very informative blog post
<a href="https://security.blogoverflow.com/2013/09/about-secure-password-hashing">About Secure Password Hashing</a>.</p>
</div>
<div class="listingblock">
<div class="title">DelegatingPasswordEncoder class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package org.springframework.security.crypto.factory;

public class PasswordEncoderFactories {
    ...
        public static PasswordEncoder createDelegatingPasswordEncoder() {
                String encodingId = &quot;bcrypt&quot;; <i class="conum" data-value="1"></i><b>(1)</b>
                Map&lt;String, PasswordEncoder&gt; encoders = new HashMap&lt;&gt;();
                encoders.put(encodingId, new BCryptPasswordEncoder()); <i class="conum" data-value="2"></i><b>(2)</b>
                encoders.put(&quot;ldap&quot;, new LdapShaPasswordEncoder());
                encoders.put(&quot;MD4&quot;, new Md4PasswordEncoder());
                encoders.put(&quot;MD5&quot;, new MessageDigestPasswordEncoder(&quot;MD5&quot;));
                encoders.put(&quot;noop&quot;, NoOpPasswordEncoder.getInstance());
                encoders.put(&quot;pbkdf2&quot;, new Pbkdf2PasswordEncoder());
                encoders.put(&quot;scrypt&quot;, new SCryptPasswordEncoder());
                encoders.put(&quot;SHA-1&quot;, new MessageDigestPasswordEncoder(&quot;SHA-1&quot;));
                encoders.put(&quot;SHA-256&quot;, new MessageDigestPasswordEncoder(&quot;SHA-256&quot;));
                encoders.put(&quot;sha256&quot;, new StandardPasswordEncoder());

                return new DelegatingPasswordEncoder(encodingId, encoders);
        }
    ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>BCrypt is the default for encrypting passwords</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Suitable encoders for decrypting are selected based on prefix in encrypted value</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>To have encrypted passwords in our MongoDB store we need to tweak our existing <em>DataInitializer</em> a bit with the
<em>PasswordEncoder</em> we just have configured.</p>
</div>
<div class="listingblock">
<div class="title">DataInitializer class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server;
...
import org.springframework.security.crypto.password.PasswordEncoder;
...

@Component
public class DataInitializer implements CommandLineRunner {

    ...
    private final PasswordEncoder passwordEncoder; <i class="conum" data-value="1"></i><b>(1)</b>

    @Autowired
    public DataInitializer(BookRepository bookRepository, UserRepository userRepository,
                            IdGenerator idGenerator, PasswordEncoder passwordEncoder) {
        ...
        this.passwordEncoder = passwordEncoder;
    }

    ...

    private void createUsers() {
        ...
        userRepository
                .save(
                        new User(
                                USER_IDENTIFIER,
                                &quot;bruce.wayne@example.com&quot;,
                                passwordEncoder.encode(&quot;wayne&quot;), <i class="conum" data-value="2"></i><b>(2)</b>
                                &quot;Bruce&quot;,
                                &quot;Wayne&quot;,
                                Collections.singletonList(Role.LIBRARY_USER)))
                .subscribe();
        ...
    }
    ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Inject <em>PasswordEncoder</em> to encrypt user passwords</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Change cleartext passwords into encrypted ones (using BCrypt as default)</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_persistent_user_storage"><a class="anchor" href="#_step_2_persistent_user_storage"></a>7.4. Step 2: Persistent User Storage</h3>
<div class="paragraph">
<p>Now that we already have configured the encoding part for passwords of our user storage
we need to connect our own user store (the users already stored in the MongoDB) with spring security&#8217;s
authentication manager.</p>
</div>
<div class="paragraph">
<p>This is done in two steps:</p>
</div>
<div class="paragraph">
<p>In the first step we need to implement spring security&#8217;s definition of a user called <em>UserDetails</em>.</p>
</div>
<div class="listingblock">
<div class="title">LibraryUser class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.security;

import com.example.library.server.dataaccess.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.stream.Collectors;

public class LibraryUser extends User implements UserDetails { <i class="conum" data-value="1"></i><b>(1)</b>

  public LibraryUser(User user) { <i class="conum" data-value="2"></i><b>(2)</b>
    super(user);
  }

  @Override
  public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
    return AuthorityUtils.commaSeparatedStringToAuthorityList(
        getRoles().stream().map(rn -&gt; &quot;ROLE_&quot; + rn.name()).collect(Collectors.joining(&quot;,&quot;)));
  }

  @Override
  public String getUsername() {
    return getEmail();
  }

  @Override
  public boolean isAccountNonExpired() {
    return true;
  }

  @Override
  public boolean isAccountNonLocked() {
    return true;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return true;
  }

  @Override
  public boolean isEnabled() {
    return true;
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>To provide our own user store we have to implement the spring security&#8217;s predefined interface <em>UserDetails</em></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The implementation for <em>UserDetails</em> is backed up by our existing <em>User</em> model</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In the second step we need to implement spring security&#8217;s interface <em>ReactiveUserDetailsService</em> to integrate our user store with the authentication manager.</p>
</div>
<div class="listingblock">
<div class="title">LibraryReactiveUserDetailsService class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.security;

import com.example.library.server.business.UserService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

@Service
public class LibraryReactiveUserDetailsService implements ReactiveUserDetailsService { <i class="conum" data-value="1"></i><b>(1)</b>

    private final UserService userService; <i class="conum" data-value="2"></i><b>(2)</b>

    public LibraryReactiveUserDetailsService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Mono&lt;UserDetails&gt; findByUsername(String username) { <i class="conum" data-value="3"></i><b>(3)</b>
        return userService.findOneByEmail(username).map(LibraryUser::new);
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>To provide our own user store we have to implement the spring security&#8217;s predefined interface <em>ReactiveUserDetailsService</em>
which is the binding component between the authentication service and our <em>LibraryUser</em></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>To search and load the targeted user for authentication we use our existing <em>UserService</em></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>This will be called when authentication happens to get user details for validating the password and
adding this user to the security context</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>After completing this part of the workshop we now still have the auto-configured <em>SecurityWebFilterChain</em> but we have
replaced the default user with our own users from our MongoDB persistent storage.</p>
</div>
<div class="paragraph">
<p>If you restart the application now you have to use the following user credentials to log in:</p>
</div>
<div class="sect3">
<h4 id="_users_and_roles"><a class="anchor" href="#_users_and_roles"></a>7.4.1. Users and roles</h4>
<div class="paragraph">
<p>There are three target user roles for this application:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>LIBRARY_USER: Standard library user who can list, borrow and return his currently borrowed books</p>
</li>
<li>
<p>LIBRARY_CURATOR: A curator user who can add, edit or delete books</p>
</li>
<li>
<p>LIBRARY_ADMIN: An administrator user who can list, add or remove users</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><em>Important:</em> We will use the following users in all subsequent labs from now on:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 2. User credentials</caption>
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Username</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Email</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Password</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Roles</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">bwayne</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:bruce.wayne@example.com">bruce.wayne@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">wayne</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">bbanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:bruce.banner@example.com">bruce.banner@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">banner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">pparker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:peter.parker@example.com">peter.parker@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">parker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_CURATOR</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ckent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:clark.kent@example.com">clark.kent@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">kent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_ADMIN</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_automatic_password_encoding_updates"><a class="anchor" href="#_automatic_password_encoding_updates"></a>7.5. Automatic Password Encoding Updates</h3>
<div class="paragraph">
<p>We already looked into the <em>DelegatingPasswordEncoder</em> and <em>PasswordEncoderFactories</em>. As these classes have knowledge
about all encryption algorithms that are supported in spring security, the framework can detect
an <em>outdated</em> encryption algorithm. By extending our already existing <em>LibraryReactiveUserDetailsService</em> class
with the additionally provided interface <em>ReactiveUserDetailsPasswordService</em> we can now enable an automatic
password encryption upgrade mechanism.</p>
</div>
<div class="paragraph">
<p>The <em>ReactiveUserDetailsPasswordService</em> interface just defines one more operation.</p>
</div>
<div class="listingblock">
<div class="title">ReactiveUserDetailsPasswordService interface</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package org.springframework.security.core.userdetails;

import reactor.core.publisher.Mono;

public interface ReactiveUserDetailsPasswordService {

        /**
         * Modify the specified user's password. This should change the user's password in the
         * persistent user repository (datbase, LDAP etc).
         *
         * @param user the user to modify the password for
         * @param newPassword the password to change to
         * @return the updated UserDetails with the new password
         */
        Mono&lt;UserDetails&gt; updatePassword(UserDetails user, String newPassword);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>First we need a user having a password that is encoded using an <em>outdated</em> hashing algorithm. We achieve this by modifying
the existing <em>DataInitializer</em> class.</p>
</div>
<div class="listingblock">
<div class="title">DataInitializer class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server;

...

import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;

...

/** Store initial users and books in mongodb. */
@Component
public class DataInitializer implements CommandLineRunner {

  ...

  private static final UUID ENCRYPT_UPGRADE_USER_IDENTIFIER =
      UUID.fromString(&quot;a7365322-0aac-4602-83b6-380bccb786e2&quot;); <i class="conum" data-value="1"></i><b>(1)</b>

  ...

  private void createUsers() {
    final Logger logger = LoggerFactory.getLogger(this.getClass());

    DelegatingPasswordEncoder oldPasswordEncoder =
        new DelegatingPasswordEncoder(
            &quot;MD5&quot;, Collections.singletonMap(&quot;MD5&quot;, new MessageDigestPasswordEncoder(&quot;MD5&quot;))); <i class="conum" data-value="2"></i><b>(2)</b>

    logger.info(&quot;Creating users with LIBRARY_USER, LIBRARY_CURATOR and LIBRARY_ADMIN roles...&quot;);
    userRepository
        .saveAll(
            Flux.just(
                ...,
                new User(   <i class="conum" data-value="3"></i><b>(3)</b>
                    ENCRYPT_UPGRADE_USER_IDENTIFIER,
                    &quot;old@example.com&quot;,
                    oldPasswordEncoder.encode(&quot;user&quot;),
                    &quot;Library&quot;,
                    &quot;OldEncryption&quot;,
                    Collections.singletonList(Role.LIBRARY_USER))))
        .log()
        .then(userRepository.count())
        .subscribe(c -&gt; logger.info(&quot;{} users created&quot;, c));
  }

  ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>We need an additional user with a password using an old encryption.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>To encrypt a user with an <em>outdated</em> password we have to add an additional password encoder for <em>MD5</em> encryption.
Never do such a thing in production. Always use the default <em>PasswordEncoderFactories</em> class instead</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Here we add another user with password encrypted by added <em>MD5</em> password encoder</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>To activate support for automatic password encoding upgrades we need to extend our existing
<em>LibraryReactiveUserDetailsService</em> class.</p>
</div>
<div class="listingblock">
<div class="title">LibraryReactiveUserDetailsService class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.security;

import com.example.library.server.business.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

@Service
public class LibraryReactiveUserDetailsService implements ReactiveUserDetailsService, ReactiveUserDetailsPasswordService { <i class="conum" data-value="1"></i><b>(1)</b>

    private static final Logger LOGGER = LoggerFactory.getLogger(LibraryReactiveUserDetailsService.class); <i class="conum" data-value="2"></i><b>(2)</b>

    ...

    @Override
    public Mono&lt;UserDetails&gt; updatePassword(UserDetails user, String newPassword) { <i class="conum" data-value="3"></i><b>(3)</b>

        LOGGER.warn(&quot;Password upgrade for user with name '{}'&quot;, user.getUsername());

        // Only for demo purposes. NEVER log passwords in production!!!
        LOGGER.info(&quot;Password upgraded from '{}' to '{}'&quot;, user.getPassword(), newPassword);

        return userService.findOneByEmail(user.getUsername())
                .doOnSuccess(u -&gt; u.setPassword(newPassword))
                .flatMap(userService::update)
                .map(LibraryUser::new);
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>To provide our own user store we have to implement the spring security&#8217;s predefined interface <em>ReactiveUserDetailsService</em>
which is the binding component between the authentication service and our <em>LibraryUser</em>. Now we also add <em>ReactiveUserDetailsPasswordService</em>
to enable automatic password encryption upgrades</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>To log the password upgrade action here we provide a logger. Please note: NEVER log passwords in production!!</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>This operation is called automatically by spring security if it detects a password that is encrypted using an
<em>outdated</em> encryption algorithm like <em>MD5</em> message digest</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now restart the application and see what happens if we try to get the list of books using this new
user (username='<a href="mailto:old@example.com">old@example.com</a>', password='user').</p>
</div>
<div class="paragraph">
<p>In the console you should see the log output showing the old <em>MD5</em> password being updated to <em>Bcrypt</em> password.</p>
</div>
<div class="admonitionblock caution">
<table>
<tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Never log any sensitive data like passwords, tokens etc., even in hashed format. Also never put such sensitive data
into your version control. And never let error details reach the client (via REST API or web application).
Make sure you disable stacktraces in client error messages using property <em>server.error.include-stacktrace=never</em></p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This is the end of lab 2 of the workshop.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You find the completed code in project <strong>lab-2/complete-library-server</strong>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In the next workshop part we also adapt the <em>SecurityWebFilterChain</em> to our needs and add authorization rules (in web and method layer)
for our application.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_lab_3_add_authorization"><a class="anchor" href="#_lab_3_add_authorization"></a>8. Lab 3: Add Authorization</h2>
<div class="sectionbody">
<div class="paragraph">
<div class="title">In this part of the workshop we want to add our customized authorization rules for our application.</div>
<p>As a result of the previous workshop steps we now have authentication for all our web endpoints
(including the actuator endpoints) and we can log in using our own users. But here security does not stop.</p>
</div>
<div class="paragraph">
<p>We know who is using our application (<strong>authentication</strong>) but we do not have control over what this user is
allowed to do in our application (<strong>authorization</strong>).</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/workshop_lab_5.png" alt="Library service authorization">
</div>
<div class="title">Figure 16. Authorization for library-service</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p><a href="https://www.owasp.org/index.php/Top_10-2017_A5-Broken_Access_Control">OWASP Top 10-2017 A5-Broken Access Control</a></p>
</div>
<div class="ulist">
<ul>
<li>
<p>With the exception of public resources, deny by default</p>
</li>
<li>
<p>Implement access control mechanisms once and re-use them throughout the application, including minimizing CORS usage.</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>As a best practice the authorization should always be implemented on different layers like the web and method layer.
This way the authorization still prohibits access even if a user manages to bypass the web url based authorization filter
by playing around with manipulated URL&#8217;s.</p>
</div>
<div class="paragraph">
<p>Our required authorization rule matrix looks like this:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 3. Authorization rules for library-server</caption>
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">URL</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Http method</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Restricted</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Roles with access</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/<strong>.css,/</strong>.jpg,/*.ico,&#8230;&#8203;</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">All</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">&#8201;&#8212;&#8201;</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/books/{bookId}/borrow</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">POST</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/books/{bookId}/return</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">POST</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/books</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">POST</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_CURATOR</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/books</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">DELETE</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_CURATOR</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/users</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">All</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_ADMIN</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/actuator/health</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GET</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">&#8201;&#8212;&#8201;</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/actuator/info</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GET</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">&#8201;&#8212;&#8201;</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/actuator/*</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">GET</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_ADMIN</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/*</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">All</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">All authenticated ones</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>All the web layer authorization rules are configured in the <em>WebSecurityConfiguration</em> class by adding
a new bean for <em>SecurityWebFilterChain</em>. Here we also already switch on the support for method layer authorization
by adding the annotation <em>@EnableReactiveMethodSecurity</em>.</p>
</div>
<div class="listingblock">
<div class="title">WebSecurityConfiguration class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.config;

...

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity <i class="conum" data-value="1"></i><b>(1)</b>
public class WebSecurityConfiguration {

    @Bean <i class="conum" data-value="2"></i><b>(2)</b>
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        return http
            .authorizeExchange()
                .matchers(PathRequest.toStaticResources().atCommonLocations())
                .permitAll() <i class="conum" data-value="3"></i><b>(3)</b>
                .matchers(EndpointRequest.to(&quot;health&quot;))
                .permitAll() <i class="conum" data-value="4"></i><b>(4)</b>
                .matchers(EndpointRequest.to(&quot;info&quot;))
                .permitAll()
                .matchers(EndpointRequest.toAnyEndpoint())
                .hasRole(Role.LIBRARY_ADMIN.name()) <i class="conum" data-value="5"></i><b>(5)</b>
                .pathMatchers(HttpMethod.POST, &quot;/books/{bookId}/borrow&quot;)
                .hasRole(Role.LIBRARY_USER.name())
                .pathMatchers(HttpMethod.POST, &quot;/books/{bookId}/return&quot;)
                .hasRole(Role.LIBRARY_USER.name()) <i class="conum" data-value="6"></i><b>(6)</b>
                .pathMatchers(HttpMethod.POST, &quot;/books&quot;)
                .hasRole(Role.LIBRARY_CURATOR.name()) <i class="conum" data-value="7"></i><b>(7)</b>
                .pathMatchers(HttpMethod.DELETE, &quot;/books&quot;)
                .hasRole(Role.LIBRARY_CURATOR.name())
                .pathMatchers(&quot;/users/**&quot;)
                .hasRole(Role.LIBRARY_ADMIN.name()) <i class="conum" data-value="8"></i><b>(8)</b>
                .anyExchange()
                .authenticated() <i class="conum" data-value="9"></i><b>(9)</b>
                .and()
                .httpBasic()
                .and()
                .formLogin() <i class="conum" data-value="10"></i><b>(10)</b>
                .and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler()) <i class="conum" data-value="11"></i><b>(11)</b>
                .and()
                .build();
    }

    @Bean <i class="conum" data-value="12"></i><b>(12)</b>
    public ServerLogoutSuccessHandler logoutSuccessHandler() {
        RedirectServerLogoutSuccessHandler logoutSuccessHandler = new RedirectServerLogoutSuccessHandler();
        logoutSuccessHandler.setLogoutSuccessUrl(URI.create(&quot;/books&quot;));
        return logoutSuccessHandler;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This adds support for method level authorization</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Configures authentication and web layer authorization for all URL&#8217;s of our REST api</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>All static resources (favicon.ico, css, images, &#8230;&#8203;) can be accessed without authentication</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Actuator endpoints for <em>health</em> and <em>info</em> can be accessed without authentication</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>All other actuator endpoints require authentication</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Borrow or returning books require authenticated user having the 'LIBRARY_USER' role</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Modifying access to books require authenticated user having the 'LIBRARY_CURATOR' role</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Access to users require authenticated user having the 'LIBRARY_ADMIN' role</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>All other web endpoints require authentication</td>
</tr>
<tr>
<td><i class="conum" data-value="10"></i><b>10</b></td>
<td>Authentication can be performed using basic authentication or form based login</td>
</tr>
<tr>
<td><i class="conum" data-value="11"></i><b>11</b></td>
<td>After logging out it redirects to URL configured in the logout success handler</td>
</tr>
<tr>
<td><i class="conum" data-value="12"></i><b>12</b></td>
<td>The configured login success handler redirects to <a href="https://localhost:8080/books">/books</a> resource</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We also add a a <em>ServerLogoutSuccessHandler</em> bean to redirect back to the <em>/books</em> endpoint after a logout
to omit the error message we got so far by redirecting to a non-existing page.</p>
</div>
<div class="paragraph">
<p>We continue with authorization on the method layer by adding the rules to our business service classes
<em>BookService</em> and <em>UserService</em>. To achieve this we use the <em>@PreAuthorize</em> annotations provided by spring security.
Same as other spring annotations (e.g. @Transactional) you can put <em>@PreAuthorize</em> annotations on global class level
or on method level.</p>
</div>
<div class="paragraph">
<p>Depending on your authorization model you may use <em>@PreAuthorize</em> to authorize using static roles or
to authorize using dynamic expressions (usually if you have roles with permissions).</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/roles_permissions.png" alt="roles_permissions">
</div>
<div class="title">Figure 17. Roles and Permissions</div>
</div>
<div class="paragraph">
<p>If you want to have a permission based authorization you can use the predefined interface <em>PermissionEvaluator</em> inside the
<em>@PreAuthorize</em> annotations like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="type">class</span> <span class="class">MyService</span> {
    <span class="annotation">@PreAuthorize</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">hasPermission(#uuid, 'user', 'write')</span><span class="delimiter">&quot;</span></span>)
    <span class="type">void</span> myOperation(<span class="predefined-type">UUID</span> uuid) {
      <span class="comment">//...</span>
    }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">PermissionEvaluator class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package org.springframework.security.access;

...
public interface PermissionEvaluator extends AopInfrastructureBean {

        boolean hasPermission(Authentication authentication, Object targetDomainObject,
                        Object permission);

        boolean hasPermission(Authentication authentication, Serializable targetId,
                        String targetType, Object permission);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the workshop due to time constraints we have to keep things simple so we just use static roles.<br>
Here it is done for the all operations of the book service.</p>
</div>
<div class="listingblock">
<div class="title">BookService class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.business;

...
import org.springframework.security.access.prepost.PreAuthorize;
...

@Service
@PreAuthorize(&quot;hasAnyRole('LIBRARY_USER', 'LIBRARY_CURATOR')&quot;) <i class="conum" data-value="1"></i><b>(1)</b>
public class BookService {

    ...

    @PreAuthorize(&quot;hasRole('LIBRARY_CURATOR')&quot;) <i class="conum" data-value="2"></i><b>(2)</b>
    public Mono&lt;Void&gt; create(Mono&lt;BookResource&gt; bookResource) {
        return bookRepository.insert(bookResource.map(this::convert)).then();
    }

    ...

    @PreAuthorize(&quot;hasRole('LIBRARY_CURATOR')&quot;) <i class="conum" data-value="3"></i><b>(3)</b>
    public Mono&lt;Void&gt; deleteById(UUID uuid) {
        return bookRepository.deleteById(uuid).then();
    }
    ...

    @PreAuthorize(&quot;hasRole('LIBRARY_USER')&quot;) <i class="conum" data-value="4"></i><b>(4)</b>
      public Mono&lt;Void&gt; borrowById(UUID bookIdentifier, UUID userIdentifier) {
      ...
    }
    ...

    @PreAuthorize(&quot;hasRole('LIBRARY_USER')&quot;) <i class="conum" data-value="5"></i><b>(5)</b>
      public Mono&lt;Void&gt; returnById(UUID bookIdentifier, UUID userIdentifier) {
      ...
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>In general all users (having either of these 2 roles) can access RESTful services for books</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Only users having role 'LIBRARY_CURATOR' can access this RESTful service to create books</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Only users having role 'LIBRARY_CURATOR' can access this RESTful service to delete books</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Only users having role 'LIBRARY_USER' can access this RESTful service to borrow books</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Only users having role 'LIBRARY_USER' can access this RESTful service to return books</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And now we add it the same way for the all operations of the user service.</p>
</div>
<div class="listingblock">
<div class="title">UserService class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.business;
...
import org.springframework.security.access.prepost.PreAuthorize;
...
@Service
@PreAuthorize(&quot;hasRole('LIBRARY_ADMIN')&quot;) <i class="conum" data-value="1"></i><b>(1)</b>
public class UserService {

    ...

    @PreAuthorize(&quot;isAnonymous() or isAuthenticated()&quot;) <i class="conum" data-value="2"></i><b>(2)</b>
    public Mono&lt;UserResource&gt; findOneByEmail(String email) {
        return userRepository.findOneByEmail(email).map(this::convert);
    }

    ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>In general only users having role 'LIBRARY_ADMIN' can access RESTful services for users</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>As this operation is used by the <em>LibraryUserDetailsService</em> to perform authentication this
has to be accessible for anonymous users (unless authentication is finished successfully anonymous
users are unauthenticated users)</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now that we have the current user context available in our application we can use this to automatically
set this user as the one who has borrowed a book or returns his borrowed book. The current user can always be evaluated
using the <em>ReactiveSecurityContextHolder</em> class. But a more elegant way is to just let the framework put the current user
directly into our operation via <em>@AuthenticationPrincipal</em> annotation.</p>
</div>
<div class="listingblock">
<div class="title">BookRestController class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.api;

import org.springframework.security.core.annotation.AuthenticationPrincipal;

@RestController
public class BookRestController {

    ...

    @PostMapping(&quot;/books/&quot; + PATH_BOOK_ID + &quot;/borrow&quot;)
    public Mono&lt;Void&gt; borrowBookById(
            @PathVariable(PATH_VARIABLE_BOOK_ID) UUID bookId, @AuthenticationPrincipal LibraryUser user) { <i class="conum" data-value="1"></i><b>(1)</b>
        return bookService.borrowById(bookId, user != null ? user.getId() : null);
    }

    @PostMapping(&quot;/books/&quot; + PATH_BOOK_ID + &quot;/return&quot;)
    public Mono&lt;Void&gt; returnBookById(@PathVariable(
            PATH_VARIABLE_BOOK_ID) UUID bookId, @AuthenticationPrincipal LibraryUser user) { <i class="conum" data-value="2"></i><b>(2)</b>
        return bookService.returnById(bookId, user != null ? user.getId() : null);
    }

    ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Now that we have an authenticated user context we can add the current user as the one to borrow a book</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Now that we have an authenticated user context we can add the current user as the one to return his borrowed a book</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>So please go ahead and re-start the application and try to borrow a book with an authenticated user.</p>
</div>
<div class="listingblock">
<div class="title">httpie get list of books</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">http localhost:9091/books --auth 'bruce.wayne@example.com:wayne'</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">httpie borrow a book</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">http POST localhost:9091/books/{bookId}/borrow --auth 'bruce.wayne@example.com:wayne'</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note: Replace <em>{bookId}</em> with the id of one of the books you have got in the list of books.</p>
</div>
<div class="listingblock">
<div class="title">curl get list of books</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">curl 'http://localhost:8080/books' -i -X GET \
    -H 'Accept: application/json' -u bruce.wayne@example.com:wayne | jq</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">curl borrow a book</div>
<div class="content">
<pre class="CodeRay highlight"><code data-lang="shell">curl 'http://localhost:8080/books/{bookId}/borrow' -i -X POST \
    -H 'Accept: application/json' -u bruce.wayne@example.com:wayne | jq</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note: Replace <em>{bookId}</em> with the id of one of the books you have got in the list of books.</p>
</div>
<div class="paragraph">
<p>At first you will notice that even with the correct basic authentication header you get an error message like this one:</p>
</div>
<div class="listingblock">
<div class="title">CSRF error output</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>POST http://localhost:8080/books

HTTP/1.1 403 Forbidden
transfer-encoding: chunked
Content-Type: text/plain
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block

No CSRF Token has been associated to this client

Response code: 403 (Forbidden)</code></pre>
</div>
</div>
<div class="paragraph">
<p>The library-server expects a CSRF token in the request but did not find one. If you use common UI frameworks like
Thymeleaf or JSF (on the serverside) or a clientside one like Angular then these already handle this CSRF processing.</p>
</div>
<div class="paragraph">
<p>In our case we do not have such handler. To successfully tra the borrow book request you have to switch off
CSRF in the library server.<br>
This is done like this in the <em>WebSecurityConfiguration</em> class.</p>
</div>
<div class="listingblock">
<div class="title">Disable CSRF</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>...
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    return http
        .csrf().disable() <i class="conum" data-value="1"></i><b>(1)</b>
        .authorizeExchange()
        .matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
    ...</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Add this line to disable CSRF.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Restart the application and retry to borrow a book. This time the request should be successful.</p>
</div>
<div class="admonitionblock caution">
<table>
<tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Do not disable CSRF on productive servers if you use session cookies, otherwise you are vulnerable to CSRF attacks.
You may safely disable CSRF for servers that use a stateless authentication approach with bearer tokens like
for OAuth2 or OpenID Connect.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In this workshop step we added the authorization to web and method layers. So now for particular RESTful endpoints access is only
permitted to users with special roles.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You find the completed code in project <strong>lab-3/complete-library-server</strong>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>But how do you know that you have implemented all the authorization rules and did not leave a big security leak
for your RESTful API? Or you may change some authorizations later by accident?</p>
</div>
<div class="paragraph">
<p>To be on a safer side here you need automatic testing. Yes, this can also be done for security!
We will see how this works in the next workshop part.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_lab_4_security_testing"><a class="anchor" href="#_lab_4_security_testing"></a>9. Lab 4: Security Testing</h2>
<div class="sectionbody">
<div class="paragraph">
<div class="title">Now it is time to prove that we have implemented these authorization rules correctly with automatic testing.</div>
<p>We start testing the rules on method layer for all operations regarding books.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/workshop_lab_6.png" alt="Library service security tests">
</div>
<div class="title">Figure 18. Automated security tests</div>
</div>
<div class="paragraph">
<p>The tests will be implemented using the new JUnit 5 version as Spring 5.0 now supports this as well.<br>
In <em>BookServiceTest</em> class we also use the new convenience annotation <em>@SpringJUnitConfig</em> which is a shortcut of
<em>@ExtendWith(value=SpringExtension.class)</em> and <em>@ContextConfiguration</em>.</p>
</div>
<div class="paragraph">
<p>As you can see in the following code only a small part is shown as a sample here to test the <em>BookService.create()</em> operation.
Authorization should always be tested for positive <strong>AND</strong> negative test cases. Otherwise you probably miss an authorization
constraint. Depending on the time left in the workshop you can add some more test cases as you like or just look into the
completed application <em>04-library-server</em>.</p>
</div>
<div class="listingblock">
<div class="title">BookServiceAuthorizationTest class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.business;

...

@DisplayName(&quot;Verify that book service&quot;)
@SpringJUnitConfig(InitialServerApplication.class) <i class="conum" data-value="1"></i><b>(1)</b>
class BookServiceAuthorizationTest {

  @Autowired private BookService bookService;

  @MockBean private BookRepository bookRepository; <i class="conum" data-value="2"></i><b>(2)</b>

  @MockBean private UserRepository userRepository;

  @DisplayName(&quot;grants access to create a book for role 'LIBRARY_CURATOR'&quot;)
  @Test
  @WithMockUser(roles = &quot;LIBRARY_CURATOR&quot;)
  void verifyCreateAccessIsGrantedForCurator() { <i class="conum" data-value="3"></i><b>(3)</b>
    when(bookRepository.insert(Mockito.&lt;Mono&lt;Book&gt;&gt;any())).thenReturn(Flux.just(new Book()));
    StepVerifier.create(
            bookService.create(
                Mono.just(
                    new Book(
                        UUID.randomUUID(),
                        &quot;123456789&quot;,
                        &quot;title&quot;,
                        &quot;description&quot;,
                        Collections.singletonList(&quot;author&quot;),
                        false,
                        null))))
        .verifyComplete();
  }

  @DisplayName(&quot;denies access to create a book for roles 'LIBRARY_USER' and 'LIBRARY_ADMIN'&quot;)
  @Test
  @WithMockUser(roles = {&quot;LIBRARY_USER&quot;, &quot;LIBRARY_ADMIN&quot;})
  void verifyCreateAccessIsDeniedForUserAndAdmin() { <i class="conum" data-value="4"></i><b>(4)</b>
    StepVerifier.create(
            bookService.create(
                Mono.just(
                    new Book(
                        UUID.randomUUID(),
                        &quot;123456789&quot;,
                        &quot;title&quot;,
                        &quot;description&quot;,
                        Collections.singletonList(&quot;author&quot;),
                        false,
                        null))))
        .verifyError(AccessDeniedException.class);
  }

  @DisplayName(&quot;denies access to create a book for anonymous user&quot;)
  @Test
  void verifyCreateAccessIsDeniedForUnauthenticated() { <i class="conum" data-value="5"></i><b>(5)</b>
    StepVerifier.create(
            bookService.create(
                Mono.just(
                    new Book(
                        UUID.randomUUID(),
                        &quot;123456789&quot;,
                        &quot;title&quot;,
                        &quot;description&quot;,
                        Collections.singletonList(&quot;author&quot;),
                        false,
                        null))))
        .verifyError(AccessDeniedException.class);
  }

  ...

}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>As this is a JUnit 5 based integration test we use <em>@SpringJUnitConfig</em> to add spring JUnit 5 extension and configure the application context</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>All data access (the repositories) is mocked</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Positive test case of access control for creating books with role 'LIBRARY_CURATOR'</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Negative test case of access control for creating books with roles 'LIBRARY_USER' or 'LIBRARY_ADMIN'</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Negative test case of access control for creating books with anonymous user</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>For sure you have to add similar tests as well for the user part.</p>
</div>
<div class="listingblock">
<div class="title">UserServiceAuthorizationTest class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.business;

...

@DisplayName(&quot;Verify that user service&quot;)
@SpringJUnitConfig(InitialServerApplication.class) <i class="conum" data-value="1"></i><b>(1)</b>
class UserServiceAuthorizationTest {

  @Autowired
  private UserService userService;

  @MockBean
  private UserRepository userRepository;

  @DisplayName(&quot;grants access to find one user by email for anonymous user&quot;)
  @Test
  void verifyFindOneByEmailAccessIsGrantedForUnauthenticated() { <i class="conum" data-value="2"></i><b>(2)</b>
    when(userRepository.findOneByEmail(any()))
        .thenReturn(
            Mono.just(
                new User(
                    UUID.randomUUID(),
                    &quot;test@example.com&quot;,
                    &quot;secret&quot;,
                    &quot;Max&quot;,
                    &quot;Maier&quot;,
                    Collections.singletonList(Role.LIBRARY_USER))));
    StepVerifier.create(userService.findOneByEmail(&quot;test@example.com&quot;))
        .expectNextCount(1)
        .verifyComplete();
  }

  @DisplayName(&quot;grants access to find one user by email for roles 'LIBRARY_USER', 'LIBRARY_CURATOR' and 'LIBRARY_ADMIN'&quot;)
  @Test
  @WithMockUser(roles = {&quot;LIBRARY_USER&quot;, &quot;LIBRARY_CURATOR&quot;, &quot;LIBRARY_ADMIN&quot;})
  void verifyFindOneByEmailAccessIsGrantedForAllRoles() { <i class="conum" data-value="3"></i><b>(3)</b>
    when(userRepository.findOneByEmail(any()))
        .thenReturn(
            Mono.just(
                new User(
                    UUID.randomUUID(),
                    &quot;test@example.com&quot;,
                    &quot;secret&quot;,
                    &quot;Max&quot;,
                    &quot;Maier&quot;,
                    Collections.singletonList(Role.LIBRARY_USER))));
    StepVerifier.create(userService.findOneByEmail(&quot;test@example.com&quot;))
        .expectNextCount(1)
        .verifyComplete();
  }

  ...

  @DisplayName(&quot;denies access to create a user for roles 'LIBRARY_USER' and 'LIBRARY_CURATOR'&quot;)
  @Test
  @WithMockUser(roles = {&quot;LIBRARY_USER&quot;, &quot;LIBRARY_CURATOR&quot;})
  void verifyCreateAccessIsDeniedForUserAndCurator() { <i class="conum" data-value="4"></i><b>(4)</b>
    StepVerifier.create(
            userService.create(
                Mono.just(
                    new User(
                        UUID.randomUUID(),
                        &quot;test@example.com&quot;,
                        &quot;secret&quot;,
                        &quot;Max&quot;,
                        &quot;Maier&quot;,
                        Collections.singletonList(Role.LIBRARY_USER)))))
        .verifyError(AccessDeniedException.class);
  }
  ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>As this is a JUnit 5 based integration test we use <em>@SpringJUnitConfig</em> to add spring JUnit 5 extension and configure the application context</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Positive test case of access control for finding a user by email for anonymous user</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Positive test case of access control for finding a user by email with all possible roles</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Negative test case of access control for creating user with roles 'LIBRARY_USER' or 'LIBRARY_CURATOR'</td>
</tr>
</table>
</div>
<div class="admonitionblock caution">
<table>
<tr>
<td class="icon">
<i class="fa icon-caution" title="Caution"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Make sure you always add positive and negative authorization tests. Otherwise you may miss
authorization endpoint leaks.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Another approach is to test the authentication for the reactive api.
This is shown in following class.</p>
</div>
<div class="listingblock">
<div class="title">BookApiAuthenticationTest</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.api;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = BookApiAuthenticationTest.TestConfig.class) <i class="conum" data-value="1"></i><b>(1)</b>
@DisplayName(&quot;Access to book api&quot;)
class BookApiAuthenticationTest {

  @Autowired private ApplicationContext applicationContext;

  private WebTestClient webTestClient;

  @MockBean private BookService bookService;
  @MockBean private UserRepository userRepository;

  @BeforeEach
  void setUp() {
    this.webTestClient =
        WebTestClient.bindToApplicationContext(applicationContext)
            .apply(springSecurity()) <i class="conum" data-value="2"></i><b>(2)</b>
            .configureClient()
            .build();
  }

  @ComponentScan(
      basePackages = {
        &quot;com.example.library.server.api&quot;,
        &quot;com.example.library.server.business&quot;,
        &quot;com.example.library.server.config&quot;
      })
  @EnableWebFlux
  @EnableWebFluxSecurity
  @EnableAutoConfiguration( <i class="conum" data-value="3"></i><b>(3)</b>
      exclude = {
        MongoReactiveAutoConfiguration.class,
        MongoAutoConfiguration.class,
        MongoDataAutoConfiguration.class,
        EmbeddedMongoAutoConfiguration.class,
        MongoReactiveRepositoriesAutoConfiguration.class,
        MongoRepositoriesAutoConfiguration.class
      })
  static class TestConfig {}

  @DisplayName(&quot;as authenticated user is granted&quot;)
  @Nested
  class AuthenticatedBookApi {

    @WithMockUser <i class="conum" data-value="4"></i><b>(4)</b>
    @Test
    @DisplayName(&quot;to get list of books&quot;)
    void verifyGetBooksAuthenticated() {

      given(bookService.findAll()).willReturn(Flux.just(BookBuilder.book().build()));

      webTestClient
          .get()
          .uri(&quot;/books&quot;)
          .accept(MediaType.APPLICATION_JSON)
          .exchange()
          .expectStatus()
          .isOk()
          .expectHeader() <i class="conum" data-value="5"></i><b>(5)</b>
          .exists(&quot;X-XSS-Protection&quot;)
          .expectHeader()
          .valueEquals(&quot;X-Frame-Options&quot;, &quot;DENY&quot;);
    }

    @Test
    @DisplayName(&quot;to get single book&quot;)
    void verifyGetBookAuthenticated() {

      UUID bookId = UUID.randomUUID();

      given(bookService.findById(bookId))
          .willReturn(Mono.just(BookBuilder.book().withId(bookId).build()));

      webTestClient
          .mutateWith(mockUser()) <i class="conum" data-value="6"></i><b>(6)</b>
          .get()
          .uri(&quot;/books/{bookId}&quot;, bookId)
          .accept(MediaType.APPLICATION_JSON)
          .exchange()
          .expectStatus()
          .isOk();
    }

    ...
  }

  @DisplayName(&quot;as unauthenticated user is denied with 401&quot;)
  @Nested
  class UnAuthenticatedBookApi {

    @Test
    @DisplayName(&quot;to get list of books&quot;)
    void verifyGetBooksUnAuthenticated() {

      webTestClient
          .get()
          .uri(&quot;/books&quot;)
          .accept(MediaType.APPLICATION_JSON)
          .exchange()
          .expectStatus()
          .isUnauthorized(); <i class="conum" data-value="7"></i><b>(7)</b>
    }

    ...
  }</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Custom test configuration</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Sets up Spring Security&#8217;s WebTestClient test support</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Exclude complete persistence layer from test (out of scope for authentication)</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Specify user authentication</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Verify existence of expected security response headers</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Alternative way to specify user authentication</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Negative test case to verify unauthenticated user is not authorized to use the api</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The testing part is the last part of adding simple username/password based security to the reactive style of the <em>library-server</em> project.
The next step will dive into the world of token-based authentication.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You find the completed code in project <strong>lab-4/complete-library-server</strong>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_oauth2openid_connect_labs"><a class="anchor" href="#_oauth2openid_connect_labs"></a>10. OAuth2/OpenID Connect Labs</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_introduction_2"><a class="anchor" href="#_introduction_2"></a>10.1. Introduction</h3>
<div class="sect3">
<h4 id="_oauth_2_0"><a class="anchor" href="#_oauth_2_0"></a>10.1.1. OAuth 2.0</h4>
<div class="paragraph">
<div class="title">In the last workshop part we will look at the new OAuth2 login client and resource server introduced in Spring Security 5.0 and 5.1.</div>
<p><a href="https://tools.ietf.org/html/rfc6749">OAuth 2.0</a> is the base protocol for authorizing 3rd party authentication services
for using business services in the internet like <a href="https://stackoverflow.com">stackoverflow</a>.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/oauth2_roles.png" alt="oauth_roles">
</div>
<div class="title">Figure 19. OAuth 2.0 role model</div>
</div>
<div class="paragraph">
<p>Authorizations are permitted via scopes that the user has to confirm before
using the requested service.</p>
</div>
<div class="paragraph">
<p>Depending on the application type OAuth 2.0 provides the following grants (flows):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://tools.ietf.org/html/rfc6749#section-4.1">Authorization Code Grant</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc7636">Authorization Code Grant with PKCE</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc6749#section-4.2">Implicit Grant</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc6749#section-4.3">Resource Owner Password Credentials Grant</a></p>
</li>
<li>
<p><a href="https://tools.ietf.org/html/rfc6749#section-4.4">Client Credentials Grant</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The following picture shows the mechanics of the <em>Authorization Code Grant Flow</em>.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/authorization_code_1.png" alt="auth_code_grant">
</div>
<div class="title">Figure 20. Authorization code grant flow</div>
</div>
</div>
<div class="sect3">
<h4 id="_openid_connect_1_0_oidc"><a class="anchor" href="#_openid_connect_1_0_oidc"></a>10.1.2. OpenID Connect 1.0 (OIDC)</h4>
<div class="paragraph">
<p><a href="https://openid.net/specs/openid-connect-core-1_0.html">OpenID Connect 1.0 (OIDC)</a> is build upon <a href="https://tools.ietf.org/html/rfc6749">OAuth2</a> and provides additional identity information
to OAuth2. For common enterprise applications that typically require authentication OpenID Connect should be used.
<a href="https://openid.net/specs/openid-connect-core-1_0.html">OIDC</a> adds <a href="https://tools.ietf.org/html/rfc7519">JSON web tokens (JWT)</a> as mandatory format for id tokens to the spec.
In OAuth2 the format of <a href="https://tools.ietf.org/html/rfc6750">bearer tokens</a> is not specified.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/oidc_roles.png" alt="openid_roles">
</div>
<div class="title">Figure 21. OpenID Connect 1.0 role model</div>
</div>
<div class="paragraph">
<p>OIDC adds an id token in addition to the access token of OAuth2 and specifies
a user info endpoint to retrieve further user information using the access token.</p>
</div>
<div class="paragraph">
<p>OIDC supports the following grant flows:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">Authorization Code Flow</a></p>
</li>
<li>
<p><a href="https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth">Implicit Flow</a></p>
</li>
<li>
<p><a href="https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth">Hybrid Flow</a></p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_tokens_in_oidc_and_oauth_2_0"><a class="anchor" href="#_tokens_in_oidc_and_oauth_2_0"></a>10.1.3. Tokens in OIDC and OAuth 2.0</h4>
<div class="paragraph">
<p>Tokens can be used in two ways:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Self-contained token (containing all information directly in token payload, e.g. JWT)</p>
</li>
<li>
<p>Reference token (token is used to look up further information)</p>
</li>
</ol>
</div>
</div>
<div class="sect3">
<h4 id="_oauth2oidc_in_spring_security_5"><a class="anchor" href="#_oauth2oidc_in_spring_security_5"></a>10.1.4. OAuth2/OIDC in Spring Security 5</h4>
<div class="paragraph">
<div class="title">Spring Security 5.0 introduced new support for OAuth2/OpenID Connect (OIDC) directly in spring security.</div>
<p>In short Spring Security 5.0 adds a completely rewritten implementation for OAuth2/OIDC which now is largely based
on a third party library <a href="https://connect2id.com/products/nimbus-oauth-openid-connect-sdk">Nimbus OAuth 2.0 SDK</a> instead of implementing all these functionality directly in Spring itself.</p>
</div>
<div class="paragraph">
<p>Spring Security 5.0 only provides the client side for servlet-based clients.</p>
</div>
<div class="paragraph">
<p>Spring Security 5.1 adds the resource server support and reactive support
for reactive clients and resource server as well.</p>
</div>
<div class="paragraph">
<p>Spring Security 5.2 adds client support for authorization code flow with PKCE.</p>
</div>
<div class="paragraph">
<p>Spring Security 5.3 will add a basic OAuth2/OIDC authorization server again (for local dev
and demos but not for productive use).</p>
</div>
<div class="paragraph">
<p>Before Spring Security 5.0 and Spring Boot 2.0 to implement OAuth2 you needed the separate project module
<a href="https://projects.spring.io/spring-security-oauth">Spring Security OAuth2</a>.</p>
</div>
<div class="paragraph">
<p>Now things have changed much, so it heavily depends now on the combination of Spring Security and Spring Boot versions
that are used how to implement OAuth2/OIDC.</p>
</div>
<div class="paragraph">
<p>Therefore you have to be aware of different approaches for Spring Security 4.x/Spring Boot 1.5.x
and Spring Security 5.x/Spring Boot 2.x.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 4. OAuth2 support in Spring Security + Spring Boot</caption>
<colgroup>
<col style="width: 16.6666%;">
<col style="width: 16.6666%;">
<col style="width: 16.6666%;">
<col style="width: 16.6666%;">
<col style="width: 16.6666%;">
<col style="width: 16.667%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Spring Security</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Spring Boot</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Client</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Resource server</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Authorization server</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Reactive (WebFlux)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">4.x</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1.5.x</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>1</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>1</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>1</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">&#8201;&#8212;&#8201;</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">5.0</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2.0.x</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>2</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">(X)<sup>3</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">(X)<sup>3</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">&#8201;&#8212;&#8201;</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">5.1</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2.1.2</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>2</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>4</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">(X)<sup>3</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>5</sup></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">5.2</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2.2.0</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>2</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>4</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">(X)<sup>3</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>5</sup></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">5.3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2.3.0</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>2</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>4</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>6</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">X<sup>5</sup></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p><sup>1</sup> Spring Boot auto-config and separate <a href="https://projects.spring.io/spring-security-oauth">Spring Security OAuth project</a><br>
<sup>2</sup> New rewritten OAuth2 login client included in Spring Security 5.0<br>
<sup>3</sup> No direct support in Spring Security 5.0/Spring Boot 2.0. For auto-configuration with Spring Boot 2.0
you still have to use the separate <a href="https://projects.spring.io/spring-security-oauth">Spring Security OAuth project</a>
together with <a href="https://github.com/spring-projects/spring-security-oauth2-boot">Spring Security OAuth2 Boot compatibilty project</a><br>
<sup>4</sup> New refactored support for resource server as part of Spring Security 5.1<br>
<sup>5</sup> OAuth2 login client and resource server with reactive support as part of Spring Security 5.1.<br>
<sup>6</sup> New OAuth2 authorization server is planned as part of Spring Security 5.2</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The OAuth2/OpenID Connect Authorization Server provided by Spring Security 5.3 will mainly suit for fast prototyping and demo
purposes. For production please use one of the <a href="https://openid.net/developers/certified/">officially certified</a> products like for example
<a href="https://www.keycloak.org/">KeyCloak</a>, <a href="https://github.com/cloudfoundry/uaa">UAA</a>, <a href="https://identityserver.io">IdentityServer</a>, <a href="https://auth0.com">Auth0</a> or <a href="https://www.okta.com/products/single-sign-on/">Okta</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>You can find more information on building OAuth2 secured microservices with Spring Boot <strong>1.5.x</strong> in</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#boot-features-security-oauth2">Spring Boot 1.5 Reference Documentation</a></p>
</li>
<li>
<p><a href="https://projects.spring.io/spring-security-oauth/docs/oauth2.html">Spring Security OAuth2 Developers Guide</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You can find more information on building OAuth2 secured microservices with Spring Boot <strong>2.1</strong>
and Spring Security <strong>5.1</strong> in</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-security-oauth2">Spring Boot 2.1 Reference Documentation</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#webflux-oauth2-login">Spring Security OAuth2/OIDC Login Client Reference Documentation</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#webflux-oauth2-resource-server">Spring Security OAuth2/OIDC Resource Server Reference Documentation</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security-oauth2-boot/docs/current/reference/htmlsingle">Spring Security OAuth Boot 2 Auto-config Documentation</a></p>
</li>
<li>
<p><a href="https://projects.spring.io/spring-security-oauth/docs/oauth2.html">Spring Security OAuth2 Developers Guide</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In this workshop we will now look at what Spring Security 5.1 provides as new
OAuth2/OIDC Login Client and Resource Server - In a reactive way.</p>
</div>
</div>
<div class="sect3">
<h4 id="_what_we_will_build"><a class="anchor" href="#_what_we_will_build"></a>10.1.5. What we will build</h4>
<div class="paragraph">
<p>In <strong>lab-5</strong> you will be provided the following sub-projects:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>initial-resource-server</strong>: The initial library server (almost similar to workshop step <strong>lab-1/initial-library-server</strong>)</p>
</li>
<li>
<p><strong>complete-resource-server</strong>: The completed OIDC resource server (as reference)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In <strong>lab-6</strong> you will be provided the following sub-projects:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>initial-oidc-client</strong>: Initial code for this workshop part to implement the new OAuth2 Login Client</p>
</li>
<li>
<p><strong>complete-oidc-client</strong>: Complete code of the new OIDC Client (as reference)</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The spring implementation of the authorization server previously used (based on Spring Boot 1.5.x) is not
fully compliant with OIDC and therefore not usable any more with OAuth2/OIDC implementation
of Spring Security 5.1.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="imageblock">
<div class="content">
<img src="images/workshop_lab_7.png" alt="OAuth2 spring roles">
</div>
<div class="title">Figure 22. Library client, service and identity provider service</div>
</div>
<div class="paragraph">
<p>These micro-services have to be configured to be reachable via the following URL addresses (Port 8080 is the default port in spring boot).</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 5. Microservice &amp; Identity Provider URL Adresses</caption>
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Service</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">URL</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Identity Management Service (Keycloak)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:8080/auth">http://localhost:8080/auth</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Library Client (OIDC Client)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:9090">http://localhost:9090</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Library Server (OIDC Resource Server)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:9091">http://localhost:9091</a></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>So now let&#8217;s start.
Again, you will just use the provided <em>keycloak</em> identity management server, the <strong>lab-5/initial-resource-server</strong>
and the <strong>lab-6/initial-oidc-client</strong> as starting point and implement
an OAuth2/OIDC resource server and client based on the project.</p>
</div>
<div class="paragraph">
<p>But first read important information about how to setup and start the required <em>keycloak</em> identity management server.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="identity-server"><a class="anchor" href="#identity-server"></a>10.2. Setup: Keycloak Identity Server</h3>
<div class="paragraph">
<p>For this workshop the OpenID Connect certified <a href="https://www.keycloak.org/">Keycloak identity provider server</a> is used.
This provider supports solutions for authentication, authorization and user administration.
For our purposes we will use this service to issue OpenID Connect compliant JSON web tokens (JWT).</p>
</div>
<div class="paragraph">
<p>To setup the local keycloak server please copy/extract it from provided USB sticks
or follow the setup instructions at <a href="https://tinyurl.com/y2mqyeua" class="bare">https://tinyurl.com/y2mqyeua</a>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You may look into <a href="https://openid.net/certification/">OpenID Connect certified products</a>
to find a suitable identity management server for your project.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Every OpenID Connect 1.0 compliant identity server must provide a page at the endpoint <em>/&#8230;&#8203;/.well-known/openid-configuration</em></p>
</div>
<div class="paragraph">
<p>To see the configuration please open the following url in your web browser:
<a href="http://localhost:8080/auth/realms/workshop/.well-known/openid-configuration">Well known OIDC configuration</a></p>
</div>
<div class="paragraph">
<p>The important information provided by this is:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 6. Identity Server Configuration</caption>
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Entry</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Value</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">issuer</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Issuer url for issued tokens by this identity server</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:8080/auth/realms/workshop" class="bare">http://localhost:8080/auth/realms/workshop</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">authorization_endpoint</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Handles authorization, usually asking for credentials and returns an authorization code</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:8080/auth/realms/workshop/protocol/openid-connect/auth" class="bare">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/auth</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">token_endpoint</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Token endpoint (exchanges given authorization code for access token)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:8080/auth/realms/workshop/protocol/openid-connect/token" class="bare">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/token</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">userinfo_endpoint</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Endpoint for requesting further user information</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:8080/auth/realms/workshop/protocol/openid-connect/userinfo" class="bare">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/userinfo</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">jwks_uri</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Uri for loading public keys to verify signatures of JSON web tokens</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://localhost:8080/auth/realms/workshop/protocol/openid-connect/certs" class="bare">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/certs</a></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>To login into your local keycloak use the following user credentials:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Username: admin</p>
</li>
<li>
<p>Password: admin</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The keycloak identity service has been preconfigured with the following user credentials
for the workshop application:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 7. User credentials</caption>
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Username</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Email</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Password</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Roles</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">bwayne</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:bruce.wayne@example.com">bruce.wayne@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">wayne</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">bbanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:bruce.banner@example.com">bruce.banner@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">banner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">pparker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:peter.parker@example.com">peter.parker@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">parker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_CURATOR</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ckent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:clark.kent@example.com">clark.kent@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">kent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_ADMIN</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="auth-code-demo"><a class="anchor" href="#auth-code-demo"></a>10.3. Intro-Lab: Authorization Code Demo Client</h3>
<div class="paragraph">
<p>In this introduction lab you can see the authorization code grant flow step-by-step in detail.</p>
</div>
<div class="paragraph">
<p>Please make sure that you have started keycloak server.
Then check this out, go to project <strong>intro-labs/auth-code-demo</strong> and run the class <em>com.example.authorizationcode.client.AuthorizationCodeDemo</em>.</p>
</div>
</div>
<div class="sect2">
<h3 id="resource-server"><a class="anchor" href="#resource-server"></a>10.4. Lab 5: OpenID Connect Resource Server</h3>
<div class="paragraph">
<p>For this workshop part the well-known library-server application is used and will be extended to act
as a OAuth2 resource server.</p>
</div>
<div class="sect3">
<h4 id="resource-server-gradle-dependencies"><a class="anchor" href="#resource-server-gradle-dependencies"></a>10.4.1. Gradle dependencies</h4>
<div class="paragraph">
<p>To use the new OAuth2 resource server support of Spring Security 5.1 you have to add the
following required dependencies to the existing gradle build file.</p>
</div>
<div class="listingblock">
<div class="title">gradle.build file</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>dependencies {
    ...
    implementation('org.springframework.boot:spring-boot-starter-oauth2-resource-server') <i class="conum" data-value="1"></i><b>(1)</b>
    testImplementation('org.springframework.security:spring-security-test') <i class="conum" data-value="2"></i><b>(2)</b>
        ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This contains all code to build an OAuth 2.0/OIDC resource server (incl. support for JOSE (Javascript Object Signing and Encryption)</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Testing support for spring security</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>These dependencies already have been added to the initial project.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You may look into the spring security oauth2 boot reference documentation
<a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-security-oauth2-server">Spring Boot 2.1 Reference Documentation</a>
and the <a href="https://docs.spring.io/spring-security/site/docs/5.1.5.RELEASE/reference/html5/#webflux-oauth2-resource-server">Spring Security 5.1 Reference Documentation</a> on how to implement a resource server.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_implementation_steps"><a class="anchor" href="#_implementation_steps"></a>10.4.2. Implementation steps</h4>
<div class="paragraph">
<p>First step is to configure an OAuth2 resource server. For this you have to register the corresponding
identity server/authorization server to use.</p>
</div>
<div class="paragraph">
<p>Spring security 5 uses the
[OpenID Connect Discovery](<a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig" class="bare">https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig</a>) specification
to completely configure the resource server to use our keycloak instance.</p>
</div>
<div class="paragraph">
<p>Navigate your web browser to the url [localhost:8080/auth/realms/workshop/.well-known/openid-configuration](<a href="http://localhost:8080/auth/realms/workshop/.well-known/openid-configuration" class="bare">http://localhost:8080/auth/realms/workshop/.well-known/openid-configuration</a>).
Then you should see the public discovery information that keycloak provides
(like the following that only shows partial information).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="json">{
  <span class="key"><span class="delimiter">&quot;</span><span class="content">issuer</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">http://localhost:8080/auth/realms/workshop</span><span class="delimiter">&quot;</span></span>,
  <span class="key"><span class="delimiter">&quot;</span><span class="content">authorization_endpoint</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/auth</span><span class="delimiter">&quot;</span></span>,
  <span class="key"><span class="delimiter">&quot;</span><span class="content">token_endpoint</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/token</span><span class="delimiter">&quot;</span></span>,
  <span class="key"><span class="delimiter">&quot;</span><span class="content">userinfo_endpoint</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/userinfo</span><span class="delimiter">&quot;</span></span>,
  <span class="key"><span class="delimiter">&quot;</span><span class="content">jwks_uri</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">http://localhost:8080/auth/realms/workshop/protocol/openid-connect/certs</span><span class="delimiter">&quot;</span></span>
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For configuring a resource server the important entries are <em>issuer</em> and <em>jwks_uri</em>.
Spring Security 5 automatically configures a resource server by just specifying the <em>issuer</em> uri value
as part of the predefined spring property <em>spring.security.oauth2.resourceserver.jwt.issuer-uri</em></p>
</div>
<div class="listingblock">
<div class="title">application.yml file</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/auth/realms/workshop <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The issuer url is used to look up the well known configuration page to get all required configuration settings to set up a resource server</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>An error you get very often with files in yaml format is that the indents are not correct.
This can lead to unexpected errors later when you try to run all this stuff.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>With this configuration in place we have already a working resource server
that can handle JWt access tokens transmitted via http bearer token header.
Spring Security also validates by default:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the JWT signature against the queried public key(s) from jwks_url</p>
</li>
<li>
<p>the JWT <em>iss</em> claim against the configured issuer uri</p>
</li>
<li>
<p>that the JWT is not expired</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The issuer URI is used to retrieve the well known OpenID Connect configuration.</p>
</div>
<div class="listingblock">
<div class="title"><a href="http://localhost:8080/auth/realms/workshop/.well-known/openid-configuration" class="bare">http://localhost:8080/auth/realms/workshop/.well-known/openid-configuration</a></div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>{
  &quot;issuer&quot;: &quot;http://localhost:8080/auth/realms/workshop&quot;,
  &quot;authorization_endpoint&quot;: &quot;http://localhost:8080/auth/realms/workshop/protocol/openid-connect/auth&quot;,
  &quot;token_endpoint&quot;: &quot;http://localhost:8080/auth/realms/workshop/protocol/openid-connect/token&quot;,
  &quot;token_introspection_endpoint&quot;: &quot;http://localhost:8080/auth/realms/workshop/protocol/openid-connect/token/introspect&quot;,
  &quot;userinfo_endpoint&quot;: &quot;http://localhost:8080/auth/realms/workshop/protocol/openid-connect/userinfo&quot;,
  &quot;end_session_endpoint&quot;: &quot;http://localhost:8080/auth/realms/workshop/protocol/openid-connect/logout&quot;,
  &quot;jwks_uri&quot;: &quot;http://localhost:8080/auth/realms/workshop/protocol/openid-connect/certs&quot;,
  &quot;check_session_iframe&quot;: &quot;http://localhost:8080/auth/realms/workshop/protocol/openid-connect/login-status-iframe.html&quot;,
  &quot;grant_types_supported&quot;: [
    &quot;authorization_code&quot;,
    &quot;implicit&quot;,
    &quot;refresh_token&quot;,
    &quot;password&quot;,
    &quot;client_credentials&quot;
  ],
  &quot;response_types_supported&quot;: [
    &quot;code&quot;,
    &quot;none&quot;,
    &quot;id_token&quot;,
    &quot;token&quot;,
    &quot;id_token token&quot;,
    &quot;code id_token&quot;,
    &quot;code token&quot;,
    &quot;code id_token token&quot;
  ],
  ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The web security configuration looks like the ones we have seen before with all that authorization
rule settings. The addition here is just for replacing the basic authentication with bearer token authentication
(expected in the http header). Additionally there are two possible alternative JWT converters referenced there.</p>
</div>
<div class="paragraph">
<p>Usually this configuration would be sufficient but as we also want to make sure that
our resource server is working with stateless token authentication we have to configure stateless
sessions (i.e. prevent <em>JSESSION</em> cookies).
Starting with Spring Boot 2 you always have to configure Spring Security
yourself as soon as you introduce a class that extends <em>WebSecurityConfigurerAdapter</em>.</p>
</div>
<div class="listingblock">
<div class="title">WebSecurityConfiguration.java file</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.config;

import com.example.library.server.common.Role;
import com.example.library.server.security.LibraryReactiveUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest;
import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfiguration {

  private final LibraryReactiveUserDetailsService libraryReactiveUserDetailsService;

  @Autowired
  public WebSecurityConfiguration(
      LibraryReactiveUserDetailsService libraryReactiveUserDetailsService) {
    this.libraryReactiveUserDetailsService = libraryReactiveUserDetailsService;
  }

  @Bean
  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .csrf()
        .disable()
        .authorizeExchange()
        .matchers(PathRequest.toStaticResources().atCommonLocations())
        .permitAll()
        .matchers(EndpointRequest.to(&quot;health&quot;))
        .permitAll()
        .matchers(EndpointRequest.to(&quot;info&quot;))
        .permitAll()
        .matchers(EndpointRequest.toAnyEndpoint())
        .hasRole(Role.LIBRARY_ADMIN.name())
        .pathMatchers(HttpMethod.POST, &quot;/books/{bookId}/borrow&quot;)
        .hasRole(Role.LIBRARY_USER.name())
        .pathMatchers(HttpMethod.POST, &quot;/books/{bookId}/return&quot;)
        .hasRole(Role.LIBRARY_USER.name())
        .pathMatchers(HttpMethod.POST, &quot;/books&quot;)
        .hasRole(Role.LIBRARY_CURATOR.name())
        .pathMatchers(HttpMethod.DELETE, &quot;/books&quot;)
        .hasRole(Role.LIBRARY_CURATOR.name())
        .pathMatchers(&quot;/users/**&quot;)
        .hasRole(Role.LIBRARY_ADMIN.name())
        .anyExchange()
        .authenticated()
        .and()
        .oauth2ResourceServer() <i class="conum" data-value="1"></i><b>(1)</b>
        .jwt() <i class="conum" data-value="2"></i><b>(2)</b>
        .jwtAuthenticationConverter(libraryUserJwtAuthenticationConverter()); <i class="conum" data-value="3"></i><b>(3)</b>
    // .jwtAuthenticationConverter(libraryUserRolesJwtAuthenticationConverter());
    return http.build();
  }

  @Bean
  public LibraryUserJwtAuthenticationConverter libraryUserJwtAuthenticationConverter() {
    return new LibraryUserJwtAuthenticationConverter(libraryReactiveUserDetailsService);
  }

  @Bean
  public LibraryUserRolesJwtAuthenticationConverter libraryUserRolesJwtAuthenticationConverter() {
    return new LibraryUserRolesJwtAuthenticationConverter(libraryReactiveUserDetailsService);
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Auto configuration for an OAuth2 resource server</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Configures JSON web token (JWT) handling for this resource server</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Configures a JWT authentication converter to map JWT to an Authentication object</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This configuration above&#8230;&#8203;</p>
</div>
<div class="ulist">
<ul>
<li>
<p>configures stateless sessions (i.e. no <em>JSESSION</em> cookies)</p>
</li>
<li>
<p>disables CSRF protection (without session cookies we do not need this any more)
(which also makes it possible to make post requests on the command line)</p>
</li>
<li>
<p>protects any request (i.e. requires authentication)</p>
</li>
<li>
<p>enables this as a resource server with expecting access tokens in JWT format</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>With mapping user information like roles you always have the choice between</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Getting the roles information from the JWT token payload</p>
</li>
<li>
<p>Getting the roles information from the mapped local persistent user</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The converter for getting roles from JWT token looks like the following:</p>
</div>
<div class="listingblock">
<div class="title">LibraryUserJwtAuthenticationConverter.java file</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.config;

import com.example.library.server.security.LibraryReactiveUserDetailsService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import reactor.core.publisher.Mono;

import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;

/** JWT converter that takes the roles from 'groups' claim of JWT token. */
public class LibraryUserJwtAuthenticationConverter
    implements Converter&lt;Jwt, Mono&lt;AbstractAuthenticationToken&gt;&gt; {
  private static final String GROUPS_CLAIM = &quot;groups&quot;;
  private static final String ROLE_PREFIX = &quot;ROLE_&quot;;

  private final LibraryReactiveUserDetailsService libraryReactiveUserDetailsService;

  public LibraryUserJwtAuthenticationConverter(
      LibraryReactiveUserDetailsService libraryReactiveUserDetailsService) {
    this.libraryReactiveUserDetailsService = libraryReactiveUserDetailsService;
  }

  @Override
  public Mono&lt;AbstractAuthenticationToken&gt; convert(Jwt jwt) { <i class="conum" data-value="1"></i><b>(1)</b>
    Collection&lt;GrantedAuthority&gt; authorities = extractAuthorities(jwt);
    return libraryReactiveUserDetailsService
        .findByUsername(jwt.getClaimAsString(&quot;email&quot;))
        .map(u -&gt; new UsernamePasswordAuthenticationToken(u, &quot;n/a&quot;, authorities));
  }

  private Collection&lt;GrantedAuthority&gt; extractAuthorities(Jwt jwt) { <i class="conum" data-value="2"></i><b>(2)</b>
    return this.getScopes(jwt).stream()
        .map(authority -&gt; ROLE_PREFIX + authority.toUpperCase())
        .map(SimpleGrantedAuthority::new)
        .collect(Collectors.toList());
  }

  @SuppressWarnings(&quot;unchecked&quot;)
  private Collection&lt;String&gt; getScopes(Jwt jwt) { <i class="conum" data-value="3"></i><b>(3)</b>
    Object scopes = jwt.getClaims().get(GROUPS_CLAIM);
    if (scopes instanceof Collection) {
      return (Collection&lt;String&gt;) scopes;
    }

    return Collections.emptyList();
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Map JWT to Authentication object with matching user and roles (Authorities) from JWT token</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Extract the scopes from JWT and map these to roles</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Get scopes from 'groups' claim</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The converter for using the roles from the mapped local user looks like this:</p>
</div>
<div class="listingblock">
<div class="title">LibraryUserRolesJwtAuthenticationConverter.java file</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.library.server.config;

import com.example.library.server.security.LibraryReactiveUserDetailsService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth2.jwt.Jwt;
import reactor.core.publisher.Mono;

/** JWT converter that takes the roles from persistent user roles. */
public class LibraryUserRolesJwtAuthenticationConverter
    implements Converter&lt;Jwt, Mono&lt;AbstractAuthenticationToken&gt;&gt; {

  private final LibraryReactiveUserDetailsService libraryReactiveUserDetailsService;

  public LibraryUserRolesJwtAuthenticationConverter(
      LibraryReactiveUserDetailsService libraryReactiveUserDetailsService) {
    this.libraryReactiveUserDetailsService = libraryReactiveUserDetailsService;
  }

  @Override
  public Mono&lt;AbstractAuthenticationToken&gt; convert(Jwt jwt) { <i class="conum" data-value="1"></i><b>(1)</b>
    return libraryReactiveUserDetailsService
        .findByUsername(jwt.getClaimAsString(&quot;email&quot;))
        .map(u -&gt; new UsernamePasswordAuthenticationToken(u, &quot;n/a&quot;, u.getAuthorities()));
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Map JWT to Authentication object with matching user and roles (Authorities) from user as well</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>To start the resource server simply run the class <em>LibraryServerApplication</em> in
project <strong>lab-5/complete-resource-server</strong>.</p>
</div>
<div class="paragraph">
<p>In the following paragraphs we now proceed to the client side of the OAuth2/OIDC part.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="oauth2-login-client"><a class="anchor" href="#oauth2-login-client"></a>10.5. Lab 6: OpenID Connect Client</h3>
<div class="sect3">
<h4 id="client-gradle-dependencies"><a class="anchor" href="#client-gradle-dependencies"></a>10.5.1. Gradle dependencies</h4>
<div class="paragraph">
<p>To use the new OAuth2 client support of Spring Security 5.1 you have to add the
following required dependencies to the existing gradle build file.</p>
</div>
<div class="listingblock">
<div class="title">gradle.build file</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>dependencies {
    ...
    implementation('org.springframework.boot:spring-boot-starter-oauth2-client') <i class="conum" data-value="1"></i><b>(1)</b>
        ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Spring Boot starter for OAuth2 client including core OAuth2/OIDC client and JOSE (Javascript Object Signing and Encryption)
framework to support for example JSON Web Token (JWT)</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_implementation_steps_2"><a class="anchor" href="#_implementation_steps_2"></a>10.5.2. Implementation steps</h4>
<div class="paragraph">
<p>First step is to configure an OAuth2/OIDC client. For this you have to register the corresponding
identity server/authorization server to use.
Here you have two possibilities:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>You can just use one of the predefined ones (Facebook, Google, etc.).</p>
</li>
<li>
<p>You register your own custom server.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Spring security provides the enumeration <em>CommonOAuth2Provider</em> which defines registration details
for a lot of well-known identity providers.</p>
</div>
<div class="listingblock">
<div class="title">CommonOAuth2Provider class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package org.springframework.security.config.oauth2.client;
...
public enum CommonOAuth2Provider {

        GOOGLE {

                @Override
                public Builder getBuilder(String registrationId) {
                        ClientRegistration.Builder builder = getBuilder(registrationId,
                                        ClientAuthenticationMethod.BASIC, DEFAULT_LOGIN_REDIRECT_URL);
                        builder.scope(&quot;openid&quot;, &quot;profile&quot;, &quot;email&quot;);
                        builder.authorizationUri(&quot;https://accounts.google.com/o/oauth2/v2/auth&quot;);
                        builder.tokenUri(&quot;https://www.googleapis.com/oauth2/v4/token&quot;);
                        builder.jwkSetUri(&quot;https://www.googleapis.com/oauth2/v3/certs&quot;);
                        builder.userInfoUri(&quot;https://www.googleapis.com/oauth2/v3/userinfo&quot;);
                        builder.userNameAttributeName(IdTokenClaimNames.SUB);
                        builder.clientName(&quot;Google&quot;);
                        return builder;
                }
        },

        GITHUB {

                @Override
                public Builder getBuilder(String registrationId) {
            ...
                }
        },

        FACEBOOK {

                @Override
                public Builder getBuilder(String registrationId) {
                    ...
                }
        },
        ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>To use one of these providers is quite easy. Just reference the enumeration constant as the provider.</p>
</div>
<div class="listingblock">
<div class="title">Google provider properties class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>spring:
  security:
    oauth2:
      client:
        registration:
          google-login:        <i class="conum" data-value="1"></i><b>(1)</b>
            provider: google <i class="conum" data-value="2"></i><b>(2)</b>
            client-id: google-client-id
            client-secret: google-client-secret</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The registration id is set to <em>google-login</em></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The provider is set to the predefined <em>google</em> client which points to <em>CommonOAuth2Provider.GOOGLE</em></td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You can find a sample application using the common provider for GitHub in project <strong>intro-labs/github-client</strong>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>But in this workshop we will focus on the second possibility and use our own custom identity provider service.<br>
To achieve this we add the following sections to the <em>application.yml</em> file.</p>
</div>
<div class="paragraph">
<p>Spring security 5 uses the
[OpenID Connect Discovery](<a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig" class="bare">https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig</a>) specification
to completely configure the client to use our keycloak instance.</p>
</div>
<div class="paragraph">
<p>For configuring an OAuth2 client the important entries are <em>issuer</em>, <em>authorization_endpoint</em>,
<em>token_endpoint</em>, <em>userinfo_endpoint</em> and <em>jwks_uri</em>.
Spring Security 5 automatically configures an OAuth2 client by just specifying the <em>issuer</em> uri value
as part of the predefined spring property <em>spring.security.oauth2.client.provider.[id].issuer-uri</em>.</p>
</div>
<div class="paragraph">
<p>For OAuth2 clients you always have to specify the client registration (with client id, client secret,
authorization grant type, redirect uri to your client callback and optionally the scope).
The client registration requires an OAuth2 provider. If you want to use your own provider you have to configure
at least the <em>issuer uri</em>. We want to change the default user name mapping for the user identity as well (
using the user name instead of the default value 'sub').</p>
</div>
<div class="listingblock">
<div class="title">application.yml client configuration</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>server:
  port: 9090

spring:
  security:
      oauth2:
        client:
          registration:
            keycloak: <i class="conum" data-value="1"></i><b>(1)</b>
              client-id: 'library-client'
              client-secret: '9584640c-3804-4dcd-997b-93593cfb9ea7'
              authorizationGrantType: authorization_code
              redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
              scope: openid
          provider:
            keycloak:
              issuerUri: http://localhost:8080/auth/realms/workshop <i class="conum" data-value="2"></i><b>(2)</b>
              user-name-attribute: name</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Client configuration like client-id and client-secret credentials and where to redirect to</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The issuer url is used to look up the well known configuration page to get all required configuration settings to set up a client</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>As the library-server is now configured as an OAuth2 resource server it requires
a valid JWT token to successfully call the <em>/books</em> endpoint now.</p>
</div>
<div class="paragraph">
<p>For all requests to the resource server we use the reactive web client, that was introduced by Spring 5.
WebClient is the successor of <em>RestTemplate</em> and works for both worlds (Servlet-based and reactive).</p>
</div>
<div class="paragraph">
<p>The next required step is to make this web client aware of transmitting the required bearer access tokens
in the <em>Authorization</em> header.</p>
</div>
<div class="paragraph">
<p>To support JWT tokens in calls we have to add a client interceptor to the <em>WebClient</em>.
The following code snippet shows how this is done:</p>
</div>
<div class="listingblock">
<div class="title">WebClientConfiguration class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.oauth2loginclient.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfiguration {

    @Bean
    WebClient webClient(ReactiveClientRegistrationRepository clientRegistrationRepository,
                        ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
        ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = <i class="conum" data-value="1"></i><b>(1)</b>
                new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
                                                                          authorizedClientRepository);
        oauth.setDefaultOAuth2AuthorizedClient(true); <i class="conum" data-value="2"></i><b>(2)</b>
        oauth.setDefaultClientRegistrationId(&quot;keycloak&quot;); <i class="conum" data-value="3"></i><b>(3)</b>
        return WebClient.builder()
                .filter(oauth) <i class="conum" data-value="4"></i><b>(4)</b>
                .build();
    }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Creates a filter for handling all the OAuth2 token stuff (i.e. initiating the OAuth2 code grant flow)</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Set this OAuth2 client as default for all requests (Do not set this if you have requests that do not require access tokens)</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Registration id for client for automatic token handling</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Add the filter to webclient</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>With these additions we add a filter function to the web client that automatically adds the
access token to all requests and also initiates the authorization grant flow if no valid
access token is available.</p>
</div>
<div class="paragraph">
<p>Finally, we need an updated client side security configuration to allow
client endpoints and enable the OAuth2 client features:</p>
</div>
<div class="listingblock">
<div class="title">SecurityConfiguration class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.oauth2loginclient.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@EnableWebFluxSecurity
@Configuration
public class SecurityConfiguration {

  @Bean
  SecurityWebFilterChain configure(ServerHttpSecurity http) {
    http.authorizeExchange().anyExchange().authenticated().and().oauth2Login().and().oauth2Client(); <i class="conum" data-value="1"></i><b>(1)</b>
    return http.build();
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Configure library client app as a OAuth2/OIDC client</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The client is build as a Thymeleaf web client. Thymeleaf basically works with HTML template files with some
specials tags to connect the template with Spring beans.</p>
</div>
<div class="paragraph">
<p>In our case there are already 3 HTML templates:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>index.html: The main template that is displayed initially to show list of books</p>
</li>
<li>
<p>createbookform.html: This renders a form to create a new book</p>
</li>
<li>
<p>users.html: This shows the list of users retrieved from the library server</p>
</li>
<li>
<p>error.html: Template to show errors</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>To map these HTML template files to the web request paths and also map the content (the 'model' as it is called
in Spring MVC) a controller class (annotated with <em>@Controller</em>) is required.</p>
</div>
<div class="paragraph">
<p>The corresponding class for the '/books' request is shown here.</p>
</div>
<div class="listingblock">
<div class="title">BookController class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.oidc.client.api;

import com.example.oidc.client.api.resource.BookResource;
import com.example.oidc.client.api.resource.CreateBookResource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;

@Controller
public class BookController { <i class="conum" data-value="1"></i><b>(1)</b>

  private final WebClient webClient;

  @Value(&quot;${library.server}&quot;)
  private String libraryServer;

  public BookController(WebClient webClient) {
    this.webClient = webClient;
  }

  @ModelAttribute(&quot;books&quot;)
  Flux&lt;BookResource&gt; books() { <i class="conum" data-value="2"></i><b>(2)</b>
    return webClient
        .get()
        .uri(libraryServer + &quot;/books&quot;)
        .retrieve()
        .onStatus(
            s -&gt; s.equals(HttpStatus.UNAUTHORIZED),
            cr -&gt; Mono.just(new BadCredentialsException(&quot;Not authenticated&quot;)))
        .onStatus(
            s -&gt; s.equals(HttpStatus.FORBIDDEN),
            cr -&gt; Mono.just(new AccessDeniedException(&quot;Not authorized&quot;)))
        .onStatus(
            HttpStatus::is4xxClientError,
            cr -&gt; Mono.just(new IllegalArgumentException(cr.statusCode().getReasonPhrase())))
        .onStatus(
            HttpStatus::is5xxServerError,
            cr -&gt; Mono.just(new Exception(cr.statusCode().getReasonPhrase())))
        .bodyToFlux(BookResource.class);
  }

  @GetMapping(&quot;/&quot;)
  Mono&lt;String&gt; index(@AuthenticationPrincipal OAuth2User user, Model model) { <i class="conum" data-value="3"></i><b>(3)</b>

    model.addAttribute(&quot;fullname&quot;, user.getName());
    model.addAttribute(
        &quot;isCurator&quot;,
        user.getAuthorities().stream().anyMatch(ga -&gt; ga.getAuthority().equals(&quot;library_curator&quot;)));
    return Mono.just(&quot;index&quot;);
  }

  @GetMapping(&quot;/createbook&quot;)
  String createForm(Model model) { <i class="conum" data-value="4"></i><b>(4)</b>

    model.addAttribute(&quot;book&quot;, new CreateBookResource());

    return &quot;createbookform&quot;;
  }

  @PostMapping(&quot;/create&quot;)
  Mono&lt;String&gt; create( <i class="conum" data-value="5"></i><b>(5)</b>
      CreateBookResource createBookResource, ServerWebExchange serverWebExchange, Model model)
      throws IOException {

    return webClient
        .post()
        .uri(libraryServer + &quot;/books&quot;)
        .body(Mono.just(createBookResource), CreateBookResource.class)
        .retrieve()
        .onStatus(
            s -&gt; s.equals(HttpStatus.UNAUTHORIZED),
            cr -&gt; Mono.just(new BadCredentialsException(&quot;Not authenticated&quot;)))
        .onStatus(
            s -&gt; s.equals(HttpStatus.FORBIDDEN),
            cr -&gt; Mono.just(new AccessDeniedException(&quot;Not authorized&quot;)))
        .onStatus(
            HttpStatus::is4xxClientError,
            cr -&gt; Mono.just(new IllegalArgumentException(cr.statusCode().getReasonPhrase())))
        .onStatus(
            HttpStatus::is5xxServerError,
            cr -&gt; Mono.just(new Exception(cr.statusCode().getReasonPhrase())))
        .bodyToMono(BookResource.class)
        .then(Mono.just(&quot;redirect:/&quot;));
  }

  @GetMapping(&quot;/borrow&quot;) <i class="conum" data-value="6"></i><b>(6)</b>
  Mono&lt;String&gt; borrowBook(@RequestParam(&quot;identifier&quot;) String identifier) {
    return webClient
        .post()
        .uri(libraryServer + &quot;/books/{bookId}/borrow&quot;, identifier)
        .retrieve()
        .onStatus(
            s -&gt; s.equals(HttpStatus.UNAUTHORIZED),
            cr -&gt; Mono.just(new BadCredentialsException(&quot;Not authenticated&quot;)))
        .onStatus(
            s -&gt; s.equals(HttpStatus.FORBIDDEN),
            cr -&gt; Mono.just(new AccessDeniedException(&quot;Not authorized&quot;)))
        .onStatus(
            HttpStatus::is4xxClientError,
            cr -&gt; Mono.just(new IllegalArgumentException(cr.statusCode().getReasonPhrase())))
        .onStatus(
            HttpStatus::is5xxServerError,
            cr -&gt; Mono.just(new Exception(cr.statusCode().getReasonPhrase())))
        .bodyToMono(BookResource.class)
        .then(Mono.just(&quot;redirect:/&quot;));
  }

  @GetMapping(&quot;/return&quot;)
  Mono&lt;String&gt; returnBook( <i class="conum" data-value="7"></i><b>(7)</b>
      @RequestParam(&quot;identifier&quot;) String identifier, ServerWebExchange serverWebExchange) {
    return webClient
        .post()
        .uri(libraryServer + &quot;/books/{bookId}/return&quot;, identifier)
        .retrieve()
        .onStatus(
            s -&gt; s.equals(HttpStatus.UNAUTHORIZED),
            cr -&gt; Mono.just(new BadCredentialsException(&quot;Not authenticated&quot;)))
        .onStatus(
            s -&gt; s.equals(HttpStatus.FORBIDDEN),
            cr -&gt; Mono.just(new AccessDeniedException(&quot;Not authorized&quot;)))
        .onStatus(
            HttpStatus::is4xxClientError,
            cr -&gt; Mono.just(new IllegalArgumentException(cr.statusCode().getReasonPhrase())))
        .onStatus(
            HttpStatus::is5xxServerError,
            cr -&gt; Mono.just(new Exception(cr.statusCode().getReasonPhrase())))
        .bodyToMono(BookResource.class)
        .then(Mono.just(&quot;redirect:/&quot;));
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Thymeleaf web controller for Books</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Use reactive webclient to call 'books' endpoint on library resource server</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Map '/' GET request to 'index.html' template</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Render the form to create new book</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Use reactive webclient to call POST 'books' endpoint on library resource server to create book</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Use reactive webclient to call POST 'books' endpoint on library resource server to borrow a book</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Use reactive webclient to call POST 'books' endpoint on library resource server to return a book</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In the client you can see the contents of the ID JWT token as well using the '/userinfo' endpoint.
This endpoint is mapped to a <em>@RestController</em>.</p>
</div>
<div class="listingblock">
<div class="title">UserInfoRestController class</div>
<div class="content">
<pre class="CodeRay highlight nowrap"><code>package com.example.oauth2loginclient.api;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import java.util.Map;

@RestController
public class UserInfoRestController {

  @GetMapping(&quot;/userinfo&quot;)
  Mono&lt;Map&lt;String, Object&gt;&gt; userInfo(@AuthenticationPrincipal OAuth2User oauth2User) {
    return Mono.just(oauth2User.getAttributes()); <i class="conum" data-value="1"></i><b>(1)</b>
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Retrieve all attributes of ID JWT token of current authenticated user</td>
</tr>
</table>
</div>
<div class="sect4">
<h5 id="_run_all_the_components"><a class="anchor" href="#_run_all_the_components"></a>Run all the components</h5>
<div class="paragraph">
<p>Finally start the two components:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Run <em>CompleteResourceServerApplication</em> class in project <strong>lab-5/complete-resource-server</strong></p>
</li>
<li>
<p>Run <em>InitialOidcClientApplication</em> class in project <strong>lab-6/initial-oidc-client</strong></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Now when you access <a href="http://localhost:9090/userinfo">localhost:9090/userinfo</a> you should be redirected to the keycloak identity server.
After logging in you should get the current authenticated user info back from identity server.</p>
</div>
<div class="paragraph">
<p>Here you can log in using one of these predefined users:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 8. User credentials</caption>
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Username</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Email</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Password</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Roles</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">bwayne</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:bruce.wayne@example.com">bruce.wayne@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">wayne</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">bbanner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:bruce.banner@example.com">bruce.banner@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">banner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_USER</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">pparker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:peter.parker@example.com">peter.parker@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">parker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_CURATOR</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ckent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="mailto:clark.kent@example.com">clark.kent@example.com</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">kent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">LIBRARY_ADMIN</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>You can now access <a href="http://localhost:9090">localhost:9090</a> as well.
This returns the book list from the library-server (resource server).</p>
</div>
</div>
<div class="sect4">
<h5 id="_logout_users"><a class="anchor" href="#_logout_users"></a>Logout Users</h5>
<div class="paragraph">
<p>After you have logged in into the library client using keycloak your session will remain valid until
the access token has expired or the session at keycloak is invalidated.</p>
</div>
<div class="paragraph">
<p>As the library client does not have a logout functionality, you have to follow the following steps to actually log out
users:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Login to keycloak <a href="http://localhost:8080/auth/admin">admin console</a> and navigate on the left to menu item <em>session</em>
Here you&#8217;ll see all user sessions (active/offline ones). By clicking on button <em>Logout all</em> you can revocate
all active sessions.</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="images/keycloak_sessions.png" alt="novatec">
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>After you have revoked sessions in keycloak you have to delete the current <em>JSESSION</em> cookie
for the library client. You can do this by opening the application tab in the developer tools of chrome.
Navigate to the cookies entry on the left and select the url of the library client, then delete the cookie
on the right hand side</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="images/devtools_cookies.png" alt="novatec">
</div>
</div>
<div class="paragraph">
<p>Now when you refresh the library client in the browser you should be redirected again to the login page of keycloak.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>You find the completed code in project <strong>lab-6/complete-oidc-client</strong>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This concludes our Spring Security 5 hands-on workshop.
I hope you have learned a lot regarding security and especially Spring Security 5.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>So take the next step and make YOUR applications more secure !</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_feedback"><a class="anchor" href="#_feedback"></a>Feedback</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you have further feedback for this workshop, suggestions for improvements or you want me to
conduct this workshop somewhere else please do not hesitate to contact me via</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Mail: <a href="mailto:andreas.falk@novatec-gmbh.de">andreas.falk@novatec-gmbh.de</a></p>
</li>
<li>
<p>Twitter: <a href="https://twitter.com/andifalk">@andifalk</a></p>
</li>
<li>
<p>LinkedIn: <a href="https://www.linkedin.com/in/andifalk">andifalk</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Thank YOU very much for being part of this workshop :-)</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_references"><a class="anchor" href="#_references"></a>References</h2>
<div class="sectionbody">
<div class="ulist bibliography">
<ul class="bibliography">
<li>
<p><a href="https://www.owasp.org/index.php/Top_10-2017_Top_10">OWASP Top 10 2017</a></p>
</li>
<li>
<p><a href="https://www.owasp.org/index.php/OWASP_Proactive_Controls#tab=Main">OWASP ProActive Controls 2018</a></p>
</li>
<li>
<p><a href="https://www.owasp.org/index.php/OWASP_Testing_Project">OWASP Testing Guide</a></p>
</li>
<li>
<p><a href="https://oauth.net/2/">OAuth2 Specifications</a></p>
</li>
<li>
<p><a href="https://openid.net/specs/openid-connect-core-1_0.html">OpenID Connect 1.0 Specification</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/">Spring Boot 1.5 Reference Guide</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/">Spring Boot 2.1 Reference Guide</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security/site/docs/4.2.x/reference/htmlsingle/">Spring Security 4.x Reference Guide</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security/site/docs/5.1.4.RELEASE/reference/html5/">Spring Security 5.1 Reference Guide</a></p>
</li>
<li>
<p><a href="https://projects.spring.io/spring-security-oauth/docs/oauth2.html"><em>Legacy</em> Spring Security OAuth Reference Guide</a></p>
</li>
<li>
<p><a href="https://docs.spring.io/spring-security-oauth2-boot/docs/current/reference/htmlsingle/"><em>Legacy</em> Spring Security OAuth2 Boot Reference Guide</a></p>
</li>
<li>
<p><a href="https://github.com/andifalk/reactive-spring-security-5-workshop">Reactive Spring Security 5 Workshop Code</a></p>
</li>
<li>
<p><a id="man"></a>[man] Jim Manico. <a href="https://www.amazon.com/Iron-Clad-Java-Building-Secure-Applications/dp/0071835881/ref=sr_1_1?ie=UTF8&amp;qid=1526999159&amp;sr=8-1&amp;keywords=ironclad+java">Iron-Clad Java: Building Secure Web Applications</a></p>
</li>
<li>
<p><a id="richer"></a>[richer] <a href="https://www.manning.com/books/oauth-2-in-action">OAuth 2 in Action (Manning Publications, ISBN: 978-1617293276)</a></p>
</li>
<li>
<p><a id="walls"></a>[walls] <a href="https://www.manning.com/books/spring-in-action-fifth-edition">Spring in Action 5th Edition (Manning Publications, ISBN: 978-1617294945</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_copyright_and_license"><a class="anchor" href="#_copyright_and_license"></a>Appendix A: Copyright and License</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Copyright &#169; 2019-2020 by Andreas Falk.
Free use of this software is being granted under the terms of the Apache 2.0 License.</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Version 1.0<br>
Last updated 2020-08-17 22:54:46 +0200
</div>
</div>
<style>
/* Stylesheet for CodeRay to match GitHub theme | MIT License | http://foundation.zurb.com */
pre.CodeRay{background:#f7f7f8}
.CodeRay .line-numbers{border-right:1px solid currentColor;opacity:.35;padding:0 .5em 0 0}
.CodeRay span.line-numbers{display:inline-block;margin-right:.75em}
.CodeRay .line-numbers strong{color:#000}
table.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.CodeRay td{vertical-align:top;line-height:inherit}
table.CodeRay td.line-numbers{text-align:right}
table.CodeRay td.code{padding:0 0 0 .75em}
.CodeRay .debug{color:#fff !important;background:#000080 !important}
.CodeRay .annotation{color:#007}
.CodeRay .attribute-name{color:#000080}
.CodeRay .attribute-value{color:#700}
.CodeRay .binary{color:#509}
.CodeRay .comment{color:#998;font-style:italic}
.CodeRay .char{color:#04d}
.CodeRay .char .content{color:#04d}
.CodeRay .char .delimiter{color:#039}
.CodeRay .class{color:#458;font-weight:bold}
.CodeRay .complex{color:#a08}
.CodeRay .constant,.CodeRay .predefined-constant{color:#008080}
.CodeRay .color{color:#099}
.CodeRay .class-variable{color:#369}
.CodeRay .decorator{color:#b0b}
.CodeRay .definition{color:#099}
.CodeRay .delimiter{color:#000}
.CodeRay .doc{color:#970}
.CodeRay .doctype{color:#34b}
.CodeRay .doc-string{color:#d42}
.CodeRay .escape{color:#666}
.CodeRay .entity{color:#800}
.CodeRay .error{color:#808}
.CodeRay .exception{color:inherit}
.CodeRay .filename{color:#099}
.CodeRay .function{color:#900;font-weight:bold}
.CodeRay .global-variable{color:#008080}
.CodeRay .hex{color:#058}
.CodeRay .integer,.CodeRay .float{color:#099}
.CodeRay .include{color:#555}
.CodeRay .inline{color:#000}
.CodeRay .inline .inline{background:#ccc}
.CodeRay .inline .inline .inline{background:#bbb}
.CodeRay .inline .inline-delimiter{color:#d14}
.CodeRay .inline-delimiter{color:#d14}
.CodeRay .important{color:#555;font-weight:bold}
.CodeRay .interpreted{color:#b2b}
.CodeRay .instance-variable{color:#008080}
.CodeRay .label{color:#970}
.CodeRay .local-variable{color:#963}
.CodeRay .octal{color:#40e}
.CodeRay .predefined{color:#369}
.CodeRay .preprocessor{color:#579}
.CodeRay .pseudo-class{color:#555}
.CodeRay .directive{font-weight:bold}
.CodeRay .type{font-weight:bold}
.CodeRay .predefined-type{color:inherit}
.CodeRay .reserved,.CodeRay .keyword {color:#000;font-weight:bold}
.CodeRay .key{color:#808}
.CodeRay .key .delimiter{color:#606}
.CodeRay .key .char{color:#80f}
.CodeRay .value{color:#088}
.CodeRay .regexp .delimiter{color:#808}
.CodeRay .regexp .content{color:#808}
.CodeRay .regexp .modifier{color:#808}
.CodeRay .regexp .char{color:#d14}
.CodeRay .regexp .function{color:#404;font-weight:bold}
.CodeRay .string{color:#d20}
.CodeRay .string .string .string{background:#ffd0d0}
.CodeRay .string .content{color:#d14}
.CodeRay .string .char{color:#d14}
.CodeRay .string .delimiter{color:#d14}
.CodeRay .shell{color:#d14}
.CodeRay .shell .delimiter{color:#d14}
.CodeRay .symbol{color:#990073}
.CodeRay .symbol .content{color:#a60}
.CodeRay .symbol .delimiter{color:#630}
.CodeRay .tag{color:#008080}
.CodeRay .tag-special{color:#d70}
.CodeRay .variable{color:#036}
.CodeRay .insert{background:#afa}
.CodeRay .delete{background:#faa}
.CodeRay .change{color:#aaf;background:#007}
.CodeRay .head{color:#f8f;background:#505}
.CodeRay .insert .insert{color:#080}
.CodeRay .delete .delete{color:#800}
.CodeRay .change .change{color:#66f}
.CodeRay .head .head{color:#f4f}
</style>
</body>
</html>